Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
6e69ecc
feat(infra): Google Cloud service tab queries
JeremyFunk Oct 8, 2026
720004a
Merge remote-tracking branch 'origin/feat/gcp-integration-card' into …
JeremyFunk Oct 8, 2026
68ba630
feat(infra): Google Cloud infra endpoints, inventory read path and ro…
JeremyFunk Oct 8, 2026
764a086
Merge remote-tracking branch 'origin/feat/gcp-dashboard-template' int…
JeremyFunk Oct 8, 2026
da440ca
feat(web): Infrastructure -> Google Cloud page
JeremyFunk Oct 8, 2026
287ac64
Merge remote-tracking branch 'origin/feat/gcp-dashboard-template' int…
JeremyFunk Oct 8, 2026
006f846
fix(infra): Google Cloud page review fixes, overview source row
JeremyFunk Oct 8, 2026
600752f
ci: run the ClickHouse SQL catalog sweep for integration query changes
JeremyFunk Oct 8, 2026
1f8f707
test(infra): pin that the Google Cloud point count decodes from a quo…
JeremyFunk Oct 8, 2026
3a35b64
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 8, 2026
9dc0f76
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
e4feada
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
97b0640
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
f85400e
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
7078247
feat(gcp): Infrastructure page follows the connection status, table p…
JeremyFunk Oct 9, 2026
8ac9c70
fix(gcp): Infrastructure page keeps the resource list out of the metr…
JeremyFunk Oct 9, 2026
7d8bcbe
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
83d4756
fix(gcp): Infrastructure notices and resources table after the second…
JeremyFunk Oct 9, 2026
8db5547
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
ecd57ee
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
b9f7041
fix(gcp): simpler resources filter
JeremyFunk Oct 9, 2026
a76b9ab
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
b71965b
fix(gcp): Infrastructure page wording
JeremyFunk Oct 9, 2026
1340e0e
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
f6d98a5
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
1f9a3e3
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
42fd92c
fix(gcp): Infrastructure page keeps what was collected when metrics a…
JeremyFunk Oct 9, 2026
e9f01da
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
25c5b80
fix(gcp): metrics-off Infrastructure page from the rendered check
JeremyFunk Oct 9, 2026
d6519b9
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
00706b1
feat(gcp): workload pages, health band and filters for Infrastructure…
JeremyFunk Oct 9, 2026
a08887f
fix(gcp): narrow the workload read by service name, keep edge buckets…
JeremyFunk Oct 9, 2026
da7f6e0
fix(gcp): an empty type or project param is no Resources filter
JeremyFunk Oct 9, 2026
9b79b9b
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
2833240
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 9, 2026
778b785
fix(gcp): link a resource to its documented console list page
JeremyFunk Oct 9, 2026
876b1f4
Merge remote-tracking branch 'origin/feat/gcp-dashboard-template' int…
JeremyFunk Oct 9, 2026
225a33d
fix(gcp): rendered-check fixes for the workload pages
JeremyFunk Oct 9, 2026
335accc
Merge remote-tracking branch 'origin/feat/gcp-infra-page' into HEAD
JeremyFunk Oct 9, 2026
a237a59
fix(gcp): gaps while a workload is silent, classes per second, scoped…
JeremyFunk Oct 9, 2026
1edd146
fix(gcp): shorter band caption
JeremyFunk Oct 9, 2026
fc4f527
fix(gcp): mark a lone point between gaps, name totals, clearer chart …
JeremyFunk Oct 9, 2026
2bbd002
Merge commit '19db2fe7932267fff54b189183468b01ccc12094' into HEAD
JeremyFunk Oct 10, 2026
220040a
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 10, 2026
ff28f03
fix(gcp): floor the presence query's start bound to whole seconds
JeremyFunk Oct 10, 2026
960f76c
perf(web): keep the Google Cloud tab model out of the startup bundle
JeremyFunk Oct 10, 2026
cdacedf
fix(web): Google Cloud list filters match the toolbar search's size
JeremyFunk Oct 10, 2026
7c1b3b8
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 10, 2026
ae92516
fix(web): a Google Cloud service that could not be read is not part o…
JeremyFunk Oct 10, 2026
be9e50a
fix(web): an empty project or region in the address is no filter on t…
JeremyFunk Oct 10, 2026
1098aee
fix(web): name the unread Google Cloud service first
JeremyFunk Oct 10, 2026
8cc6c25
refactor(web): Google Cloud infra pages use the primitives their sibl…
JeremyFunk Oct 10, 2026
947d6e0
refactor(web): a Google Cloud workload page states its facts under it…
JeremyFunk Oct 10, 2026
03ee7a9
refactor(web): the Google Cloud integration page shows workloads and …
JeremyFunk Oct 10, 2026
e06d716
fix(web): the Google Cloud integration page says when a service's met…
JeremyFunk Oct 10, 2026
cb3dcdb
fix(web): the GKE container logs notice says when the logs arrive twi…
JeremyFunk Oct 10, 2026
5472320
feat(gcp): the log filter is a choice per runtime, not a switch for G…
JeremyFunk Oct 10, 2026
7290a4c
fix(web): the Google Cloud infra page no longer ages a healthy connec…
JeremyFunk Oct 10, 2026
20b6aa7
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 14, 2026
f97865f
fix(web): the Google Cloud fleet band says when a service's metrics c…
JeremyFunk Oct 14, 2026
fd3d851
fix(gcp): count name-filtered resources with their own query, not the…
JeremyFunk Oct 14, 2026
0764947
chore(web): raise the startup bundle budget to 699 KB for the Google …
JeremyFunk Oct 14, 2026
1749421
test(web): the interval refresh catches up when a hidden tab returns,…
JeremyFunk Oct 14, 2026
f6312ae
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 14, 2026
e94b38d
feat(gcp): application output goes over OpenTelemetry, Google Cloud f…
JeremyFunk Oct 14, 2026
e55428d
Merge remote-tracking branch 'origin/feat/gcp-infra-page' into feat/g…
JeremyFunk Oct 14, 2026
f13e8d8
feat(infra): drop the Google Cloud workload health band and its scope…
JeremyFunk Oct 14, 2026
67e96e2
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
73e6ce7
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
c8d69f8
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
cef1419
refactor(gcp): semconv-style metric names on the infrastructure page
JeremyFunk Oct 15, 2026
b6f586a
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
57767e1
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
a4a99a6
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
b86fa87
Merge feat/gcp-dashboard-template into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
8a2f2fd
Merge remote-tracking branch 'origin/feat/gcp-dashboard-template' int…
JeremyFunk Oct 15, 2026
1b75ecf
Merge branch 'feat/gcp-dashboard-template' into HEAD
JeremyFunk Oct 15, 2026
0fc4554
fix(ci): 1 KB headroom on the bundle budget; CI measures the PR merge…
JeremyFunk Oct 15, 2026
787de12
fix(ci): resolve the bundle budget merge
JeremyFunk Oct 15, 2026
40c845d
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 15, 2026
08a505c
feat(infra): Google Cloud page in the Cloudflare layout, Cloud SQL to…
JeremyFunk Oct 15, 2026
29c0215
chore(web): raise the startup budget for the Google Cloud page redesign
JeremyFunk Oct 15, 2026
aac774b
feat(infra): Google Cloud list page rail with console links
JeremyFunk Oct 16, 2026
3cec807
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
6d7e999
feat(infra): drop the Google Cloud list rail, plainer related links o…
JeremyFunk Oct 16, 2026
b5a69fd
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
6be9363
feat(infra): service icons, console deeplinks and links in the rail o…
JeremyFunk Oct 16, 2026
c5b4fdb
Merge remote-tracking branch 'origin/feat/gcp-dashboard-template' int…
JeremyFunk Oct 16, 2026
83cfdae
Merge commit '27e78fe0929bb9d6290c64aac9c4e996e959de97' into HEAD
JeremyFunk Oct 16, 2026
7906d3d
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
658b4e0
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
8bb8c6e
fix(infra): cap Google Cloud overview findings, keep a Cloud SQL inst…
JeremyFunk Oct 16, 2026
5071eec
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
2a72ccc
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
7d944df
feat(infra): Cloud Run mark, title beside the time controls, toned ra…
JeremyFunk Oct 16, 2026
3d30c62
fix(infra): type the Cloud SQL candidates and an absent project in co…
JeremyFunk Oct 16, 2026
1f8478b
fix(infra): no "sent no spans" on a Google Cloud workload while the s…
JeremyFunk Oct 16, 2026
0620e87
fix(infra): unique keys for repeated Cloud SQL rail rows, match the l…
JeremyFunk Oct 16, 2026
03d7dda
Merge branch 'feat/gcp-dashboard-template' into feat/gcp-infra-page
JeremyFunk Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ jobs:
# it. Without these two the PR that broke every main chart
# would still skip the only job that could have caught it.
- 'packages/query-engine/**'
# The sweep analyzes the integration builders too.
- 'packages/query-engine-integrations/**'
- 'packages/domain/src/clickhouse/**'
- 'packages/domain/src/tinybird/**'
# The migrations this job replays, and the datasource
Expand Down
9 changes: 9 additions & 0 deletions apps/api/src/routes/internal/integrations.http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
CloudflareUsageResponse,
CurrentTenant,
ExternalUserId,
GcpResourcesResponse,
GithubDeleteRepositoryResponse,
GithubDisconnectResponse,
GithubIntegrationStatus,
Expand Down Expand Up @@ -54,6 +55,7 @@ import {
topTrafficQuery,
type TopTrafficGroupDefinition,
} from "@maple/backend/services/integrations/cloudflare-analytics/queries"
import { GcpConnectorService } from "@maple/backend/services/integrations/GcpConnectorService"
import { RailwayMetricsService } from "@maple/backend/services/integrations/RailwayMetricsService"
import { GithubConnectService } from "@maple/backend/services/integrations/vcs/vendor/github/GithubConnectService"
import { VcsCommitService } from "@maple/backend/services/integrations/vcs/VcsCommitService"
Expand Down Expand Up @@ -118,6 +120,7 @@ export const HttpIntegrationsLive = HttpApiBuilder.group(MapleInternalApi, "inte
const cloudflare = yield* CloudflareOAuthService
const cloudflareAnalytics = yield* CloudflareAnalyticsService
const railway = yield* RailwayMetricsService
const gcp = yield* GcpConnectorService
const edgeCache = yield* EdgeCacheService
const env = yield* Env

Expand Down Expand Up @@ -464,6 +467,12 @@ export const HttpIntegrationsLive = HttpApiBuilder.group(MapleInternalApi, "inte
return new RailwayDisconnectResponse(yield* railway.disconnect(tenant.orgId))
}),
)
.handle("gcpResources", ({ query }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
return new GcpResourcesResponse(yield* gcp.resources(tenant.orgId, query))
}),
)
.handle("githubStatus", () =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
Expand Down
58 changes: 58 additions & 0 deletions apps/api/src/routes/internal/query-engine.http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import {
ServiceApdexResponse,
PlanetScaleInfraTimeseriesResponse,
RailwayInfraRowsResponse,
GcpInfraMetricsResponse,
GcpInfraPresenceResponse,
GcpInfraSparklinesResponse,
GcpCloudSqlDatabasesResponse,
GcpCloudSqlTopQueriesResponse,
GcpInfraTimeseriesResponse,
ServiceCloudflareStatsResponse,
ServicePlanetScaleStatsResponse,
CloudflareInfraZonesResponse,
Expand Down Expand Up @@ -97,6 +103,7 @@ import {
TraceId,
SpanId,
} from "@maple/domain/http"
import { GCP_INFRA_SERVICE_IDS, gcpInfraMetrics } from "@maple/domain/gcp-infra"
import { SESSION_LIVE_WINDOW_SECONDS } from "@maple/domain/query-engine"
import { isAiContentFormat } from "@maple/domain/ai-traffic"
import { Cause, Clock, DateTime, Effect, Option, Schema } from "effect"
Expand Down Expand Up @@ -848,6 +855,57 @@ export const HttpQueryEngineLive = HttpApiBuilder.group(MapleInternalApi, "query
})
}),
)
.handle("gcpInfraPresence", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
const rows = yield* runQuery(Queries.gcpInfraPresence, tenant, payload)
const reported = new Set(rows.map((row) => row.metric))
return new GcpInfraPresenceResponse({
services: GCP_INFRA_SERVICE_IDS.filter((service) =>
gcpInfraMetrics(service).some((metric) => reported.has(metric.name)),
),
})
}),
)
.handle("gcpInfraMetrics", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
const rows = yield* runQuery(Queries.gcpInfraMetrics, tenant, payload)
return new GcpInfraMetricsResponse({ data: rows })
}),
)
.handle("gcpInfraTimeseries", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
const rows = yield* runQuery(Queries.gcpInfraTimeseries, tenant, payload)
return new GcpInfraTimeseriesResponse({
data: rows.map((row) => ({ ...row, bucket: DateTime.formatIso(row.bucket) })),
})
}),
)
.handle("gcpInfraSparklines", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
const rows = yield* runQuery(Queries.gcpInfraSparklines, tenant, payload)
return new GcpInfraSparklinesResponse({
data: rows.map((row) => ({ ...row, bucket: DateTime.formatIso(row.bucket) })),
})
}),
)
.handle("gcpCloudSqlTopQueries", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
const rows = yield* runQuery(Queries.gcpCloudSqlTopQueries, tenant, payload)
return new GcpCloudSqlTopQueriesResponse({ data: rows })
}),
)
.handle("gcpCloudSqlDatabases", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
const rows = yield* runQuery(Queries.gcpCloudSqlDatabases, tenant, payload)
return new GcpCloudSqlDatabasesResponse({ databases: rows.map((row) => row.database) })
}),
)
.handle("cloudflareInfraZones", ({ payload }) =>
Effect.gen(function* () {
const tenant = yield* CurrentTenant.Context
Expand Down
88 changes: 88 additions & 0 deletions apps/api/src/routes/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import type {
CloudflareInfraZoneTimeseriesRequest,
CloudflareInfraZonesRequest,
ContainerInfraTimeseriesRequest,
GcpInfraMetricsRequest,
GcpInfraPresenceRequest,
GcpCloudSqlInstanceRequest,
GcpInfraSparklinesRequest,
GcpInfraTimeseriesRequest,
GetLogRequest,
NodeInfraTimeseriesRequest,
PlanetScaleInfraTimeseriesRequest,
Expand Down Expand Up @@ -289,6 +294,83 @@ const railwayInfraServiceTimeseries = defineQuery({
}),
})

// A small read of the hourly catalog that decides which tabs /infra/gcp shows.
const gcpInfraPresence = defineQuery({
id: "gcpInfraPresence",
profile: "discovery",
cache: 60,
compile: (payload: GcpInfraPresenceRequest, orgId: OrgId) =>
CH.compile(Integrations.gcpInfraPresenceSQL(), {
orgId,
startTime: payload.startTime,
endTime: payload.endTime,
}),
})

const gcpInfraMetrics = defineQuery({
id: "gcpInfraMetrics",
profile: "aggregation",
cache: 15,
compile: (payload: GcpInfraMetricsRequest, orgId: OrgId) =>
CH.compile(Integrations.gcpInfraMetricsSQL(payload.service), {
orgId,
startTime: payload.startTime,
endTime: payload.endTime,
}),
})

const gcpInfraTimeseries = defineQuery({
id: "gcpInfraTimeseries",
profile: "aggregation",
cache: 15,
compile: (payload: GcpInfraTimeseriesRequest, orgId: OrgId) =>
CH.compile(Integrations.gcpInfraTimeseriesSQL(payload.source, payload.keys), {
orgId,
startTime: payload.startTime,
endTime: payload.endTime,
// The poller stores one point per minute.
bucketSeconds: Math.max(60, payload.bucketSeconds),
}),
})

const gcpInfraSparklines = defineQuery({
id: "gcpInfraSparklines",
profile: "aggregation",
cache: 15,
compile: (payload: GcpInfraSparklinesRequest, orgId: OrgId) =>
CH.compile(Integrations.gcpInfraSparklinesSQL(payload.service), {
orgId,
startTime: payload.startTime,
endTime: payload.endTime,
// The poller stores one point per minute.
bucketSeconds: Math.max(60, payload.bucketSeconds),
}),
})

const gcpCloudSqlTopQueries = defineQuery({
id: "gcpCloudSqlTopQueries",
profile: "aggregation",
cache: 15,
compile: (payload: GcpCloudSqlInstanceRequest, orgId: OrgId) =>
CH.compile(Integrations.gcpCloudSqlTopQueriesSQL(payload.keys), {
orgId,
startTime: payload.startTime,
endTime: payload.endTime,
}),
})

const gcpCloudSqlDatabases = defineQuery({
id: "gcpCloudSqlDatabases",
profile: "list",
cache: 15,
compile: (payload: GcpCloudSqlInstanceRequest, orgId: OrgId) =>
CH.compile(Integrations.gcpCloudSqlDatabasesSQL(payload.keys), {
orgId,
startTime: payload.startTime,
endTime: payload.endTime,
}),
})

const zoneDetailParams = (payload: CloudflareInfraZoneDetailRequest, orgId: OrgId) => ({
orgId,
serviceName: payload.serviceName,
Expand Down Expand Up @@ -808,6 +890,12 @@ export const Queries = {
planetscaleInfraTimeseries,
railwayInfraServices,
railwayInfraServiceTimeseries,
gcpInfraPresence,
gcpInfraMetrics,
gcpInfraTimeseries,
gcpInfraSparklines,
gcpCloudSqlTopQueries,
gcpCloudSqlDatabases,

// ZoneDetail / PlanetScaleStats sub-queries, declared above.
cloudflareInfraZoneDetailStatus,
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/routes/v2/integrations-gcp.http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ describe("v2 gcp integration over HTTP", () => {
const path = `${CONNECTORS}/${created.body.id}`

const both = await harness.request("POST", `${path}/setup_scripts`, admin.secret, {
log_filter: "include_gke_container_logs",
application_logs: ["cloud_run", "cloud_functions", "app_engine", "gke"],
})
expect(both.status).toBe(200)
expect(Object.keys(both.body).sort()).toEqual(["cleanup_script", "object", "setup_script"])
Expand All @@ -236,13 +236,13 @@ describe("v2 gcp integration over HTTP", () => {
// The cleanup script tells Maple that it ran, with the connector's secret.
expect(both.body.cleanup_script).toContain("?secret=maple_gcp_")

// Without an option an existing sink keeps its filter and a new one gets the default, which
// Without the list an existing sink keeps its filter and a new one gets the default, which
// leaves out GKE container logs; with logs off there is none to carry.
const kept = await harness.request("POST", `${path}/setup_scripts`, admin.secret, {})
expect(kept.body.setup_script).toContain("LOG_FILTER_MODE='keep'")
expect(kept.body.setup_script).toContain(` AND NOT resource.type="k8s_container"'\n`)
const unknown = await harness.request("POST", `${path}/setup_scripts`, admin.secret, {
log_filter: "everything",
application_logs: ["everything"],
})
expect(unknown.status).toBe(400)

Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/routes/v2/integrations-gcp.http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const HttpV2GcpIntegrationsLive = HttpApiBuilder.group(MapleApiV2, "gcpIn
const tenant = yield* CurrentTenant.Context
yield* requireAdmin(tenant.roles, adminOnly)
const scripts = yield* gcp.scripts(tenant.orgId, params.id, {
logFilter: payload.log_filter ?? "keep",
applicationLogs: payload.application_logs ?? null,
})
return {
object: "gcp_connector.setup_scripts" as const,
Expand Down
48 changes: 26 additions & 22 deletions apps/landing/src/content/docs/integrations/gcp-opentelemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Your workloads send traces, logs and metrics straight to Maple over OpenTelemetr
### GKE

1. Instrument each service with an OpenTelemetry SDK. Export to Maple, or to the [Maple Kubernetes collector](/docs/infrastructure/kubernetes) in the cluster.
2. [Connect Google Cloud](/docs/integrations/gcp#connect) and keep the log filter on **Recommended: no GKE container logs**.
2. [Connect Google Cloud](/docs/integrations/gcp#connect) and leave **Log filter** as it is: no application output is forwarded.

The connection then adds node and cluster logs, audit logs and the `gcp.gke.*` metrics.

### Cloud Run

1. Instrument the service with an OpenTelemetry SDK that exports to Maple, with the settings under [Instrument a Cloud Run service](#instrument-a-cloud-run-service).
2. [Connect Google Cloud](/docs/integrations/gcp#connect).
3. If the service exports logs over OpenTelemetry, [leave out its container output](#change-the-filter) too: the recommended filter forwards what a Cloud Run container writes to stdout and stderr.
3. If the service exports logs over OpenTelemetry, untick **Cloud Run** under [**Log filter**](#change-the-filter): by default the connection forwards what a Cloud Run container writes to stdout and stderr.

The connection adds Cloud Run's request logs and the `gcp.cloud_run.*` metrics. Cloud Run sends a `traceparent` header with every request. An SDK that continues it gives the server span the trace ID of Google's request log for that request, so the request log, your spans and your log lines join on one trace.

Expand All @@ -40,35 +40,39 @@ The connection adds Cloud Run's request logs and the `gcp.cloud_run.*` metrics.

The connection adds audit logs and the `gcp.gce.*` metrics.

## GKE container logs
## Application output

The recommended filter leaves out `resource.type="k8s_container"`: what the containers of a GKE cluster write to stdout and stderr.
Application output is what the code of a runtime writes to stdout and stderr. The connection forwards none by default: send it over OpenTelemetry. Platform, request and audit logs are forwarded either way. For workloads that can't, click **Can't forward via OpenTelemetry** under **Log filter** and tick their runtimes.

| Reason | Detail |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Duplicates | A workload that sends its logs over OpenTelemetry, or a collector that reads pod logs, already delivers each line to Maple. Forwarding it from Google Cloud stores it twice. |
| No trace link | The OpenTelemetry copy carries the trace and span ID of the request that wrote it. The copy from Google Cloud has neither. |
| Volume | Container output grows with traffic. Every forwarded line counts toward your Maple plan and toward the Pub/Sub usage Google bills to your account. |
| Runtime | Left out when unticked | Still forwarded |
| ------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| **Cloud Run** | `run.googleapis.com/stdout` and `stderr` of services, jobs, worker pools and 2nd gen functions | Request logs, system logs, audit logs |
| **Cloud Functions** | `cloudfunctions.googleapis.com/cloud-functions` of 1st gen functions, with its start and finish lines | Audit logs |
| **App Engine** | `stdout` and `stderr` of `gae_app` | Request logs, audit logs |
| **GKE containers** | Everything of `resource.type="k8s_container"` | Node, pod and cluster events, audit logs |

Include GKE container logs when the workloads don't send their logs over OpenTelemetry and no collector reads them. Cloud Logging is then the only place those lines exist.
Don't tick a runtime whose workloads already send their logs to Maple:

## Change the filter
| Reason | Detail |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Duplicates | A workload that exports logs over OpenTelemetry and also prints them, or a collector that reads pod logs, already delivers each line. Forwarding it from Google Cloud stores it twice. |
| No trace link | The OpenTelemetry copy carries the trace and span ID of the request that wrote it. The copy from Google Cloud has neither. |
| Volume | Application output grows with traffic. Every forwarded line counts toward your Maple plan and toward the Pub/Sub usage Google bills to your account. |

Maple does not check which workloads send their logs another way. A runtime with both kinds of workloads is one tick: tick it when the uninstrumented ones matter more, since Cloud Logging is the only place their lines exist.

On the connection's card, click **Configure** and choose a filter under **Log filter**. Click **Continue to the script**, then copy the script and run it in Cloud Shell again.
Other places your code runs have no tick and are always forwarded: Compute Engine logs collected by the Ops Agent, and the job output of Batch, Dataflow, Dataproc, Cloud Composer and Vertex AI. Leave them out with a filter of your own.

| Log filter | The sink forwards |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Recommended: no GKE container logs** | Platform logs. Leaves out GKE container logs and the [other exclusions](/docs/integrations/gcp#logs). |
| **Include GKE container logs** | The same, plus GKE container logs. Maple asks you to acknowledge the duplicates before you continue. |
| **Keep the sink's current filter** | What it forwards now. Offered, and preselected, once a run has set log forwarding up. |
## Change the filter

For any other filter, paste the script into an editor, edit `LOG_FILTER` near its top, set `LOG_FILTER_MODE` to `set` and run it. To also leave out Cloud Run container output, for services that send their logs over OpenTelemetry, add this at the end of `LOG_FILTER`, inside the quotes:
On the connection's row, click **Configure**. Under **Log filter**, choose **Replace filter**. To forward application output, click **Can't forward via OpenTelemetry** and tick its runtimes. Click **Continue to the script**, then copy the script and run it in Cloud Shell again.

```text
AND NOT log_id("run.googleapis.com/stdout") AND NOT log_id("run.googleapis.com/stderr")
```
| Log filter | The sink forwards |
| ----------------------- | ------------------------------------------------------------------------------------- |
| **Keep current filter** | What it forwards now. Preselected once a run has set log forwarding up. |
| **Replace filter** | Platform, request and audit logs, plus the application output of any ticked runtimes. |

Cloud Run's request logs are in `run.googleapis.com/requests` and still pass. The filter uses the [Logging query language](https://cloud.google.com/logging/docs/view/logging-query-language).
For any other filter, paste the script into an editor, edit `LOG_FILTER` near its top, set `LOG_FILTER_MODE` to `set` and run it. The filter uses the [Logging query language](https://cloud.google.com/logging/docs/view/logging-query-language).

## Instrument a Cloud Run service

Expand Down
Loading
Loading