Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: otel collector integration #4769

Merged
merged 37 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
082a29d
chore: WIP for an otel-collector provider
mkhq Jun 27, 2023
bca0636
feat: create exporter that can be configured at a later point and the…
TimBeyer Jun 21, 2023
1abeef4
feat: send traces to OTLP exporter once collector is spawned
TimBeyer Jun 28, 2023
478932c
feat: wait for initialization of collector
TimBeyer Jun 29, 2023
68261fe
chore: remove redundant createModuleTypes
TimBeyer Jun 29, 2023
79fa871
feat: create temporary config if it does not exist
TimBeyer Jun 29, 2023
7be8710
wip: start creating config for datadog
TimBeyer Jul 3, 2023
3ae034b
feat: ensure that async hooks are run when the process is shut down
TimBeyer Jul 4, 2023
e6f650f
feat: log logged processes output with silly loglevel
TimBeyer Jul 4, 2023
03c04ff
feat: ensure collector has enough time to terminate
TimBeyer Jul 4, 2023
88ff550
feat: wait for datadog export log line explicitly
TimBeyer Jul 4, 2023
4ec753e
refactor: simplify config generation and get actual hostname
TimBeyer Jul 4, 2023
7ce0b9d
feat: make datadog configurable from the API secrets
TimBeyer Jul 4, 2023
2ac7013
feat: support multiple concurrent http exporters, refactor config
TimBeyer Jul 4, 2023
30bcc10
refactor: extract out base config
TimBeyer Jul 4, 2023
51eb179
feat: trace the otel collector startup times
TimBeyer Jul 4, 2023
8722658
feat: configure exporters in provider config
TimBeyer Jul 5, 2023
9c9691c
feat: timeout for final datadog sync
TimBeyer Jul 5, 2023
a171e71
feat: wait for collector process to terminate
TimBeyer Jul 5, 2023
b0ad45f
feat: add honeycomb support
TimBeyer Jul 5, 2023
d917f1f
fix: bind to localhost, handle empty exporters
mkhq Jul 6, 2023
f0702b3
fix: otel provider example and added the logging exporter
mkhq Jul 6, 2023
72acab8
chore: remove unused import
TimBeyer Jul 7, 2023
eea23f2
fix: work around anyOf validation to schema conversion
TimBeyer Jul 7, 2023
c1deba3
fix: license headers
TimBeyer Jul 7, 2023
bbd6ffa
chore: code formatting
TimBeyer Jul 7, 2023
0a5e483
docs: update docs
TimBeyer Jul 7, 2023
0d1136e
chore: remove two todos
TimBeyer Jul 10, 2023
a1c3d1d
chore: remove projectId from plugin context
TimBeyer Jul 10, 2023
875467b
refactor: move validators into exporter modules and use those for the…
TimBeyer Jul 12, 2023
7234d73
feat: always enable tracing by default but no-op when it's not config…
TimBeyer Jul 12, 2023
366bf0f
fix: remove console.log statements
TimBeyer Jul 12, 2023
de50fcc
feat: address review comments
TimBeyer Jul 13, 2023
e6b5cff
fix: make sure when there are no active otel exporters the no-op expo…
TimBeyer Jul 13, 2023
72fedd4
test: e2e smoke test for otel exporter
TimBeyer Jul 14, 2023
5b57c87
fix: correct hostname for test
TimBeyer Jul 14, 2023
e90d92d
fix: explicitly forward log events from provider instead of having `s…
TimBeyer Jul 14, 2023
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
5 changes: 5 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,11 @@ workflows:
name: e2e-vote-helm
project: vote-helm
requires: [build]
- e2e-project:
<<: *only-internal-prs
name: e2e-open-telemetry
project: open-telemetry
requires: [build]
# - e2e-project:
# <<: *only-internal-prs
# name: e2e-vote-helm-modules
Expand Down
2 changes: 1 addition & 1 deletion cli/bin/garden
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ if (process.env.GARDEN_ENABLE_PROFILING === "1" || process.env.GARDEN_ENABLE_PRO
}
}

const { initTracing } = require("@garden-io/core/build/src/util/tracing/tracing")
const { initTracing } = require("@garden-io/core/build/src/util/open-telemetry/tracing")
initTracing()

require("source-map-support").install()
Expand Down
6 changes: 3 additions & 3 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { shutdown } from "@garden-io/core/build/src/util/util"
import { GardenCli, RunOutput } from "@garden-io/core/build/src/cli/cli"
import { GardenPluginReference } from "@garden-io/core/build/src/plugin/plugin"
import { GlobalConfigStore } from "@garden-io/core/build/src/config-store/global"
import { getOtelSDK } from "@garden-io/core/build/src/util/tracing/tracing"
import { withContextFromEnv } from "@garden-io/core/build/src/util/tracing/propagation"
import { wrapActiveSpan } from "@garden-io/core/build/src/util/tracing/spans"
import { getOtelSDK } from "@garden-io/core/build/src/util/open-telemetry/tracing"
import { withContextFromEnv } from "@garden-io/core/build/src/util/open-telemetry/propagation"
import { wrapActiveSpan } from "@garden-io/core/build/src/util/open-telemetry/spans"

// These plugins are always registered
export const getBundledPlugins = (): GardenPluginReference[] => [
Expand Down
5 changes: 3 additions & 2 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@
"@opentelemetry/semantic-conventions": "^1.14.0",
"@opentelemetry/instrumentation-http": "^0.40.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.40.0",
"@opentelemetry/otlp-exporter-base": "^0.40.0",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"analytics-node": "3.5.0",
"archiver": "^5.3.1",
"async": "^3.2.4",
"async-exit-hook": "^2.0.1",
"@scg82/exit-hook": "^3.4.1",
"async-lock": "^1.4.0",
"bluebird": "^3.7.2",
"certpem": "^1.1.3",
Expand Down Expand Up @@ -248,7 +249,7 @@
"testdouble-chai": "^0.5.0",
"timekeeper": "^2.2.0",
"touch": "^3.1.0",
"type-fest": "^2.19.0",
"type-fest": "^3.12.0",
"typescript": "^5.1.3",
"utility-types": "^3.10.0"
},
Expand Down
4 changes: 2 additions & 2 deletions core/src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ import { dedent } from "../util/string"
import { GardenProcess, GlobalConfigStore } from "../config-store/global"
import { registerProcess, waitForOutputFlush } from "../process"
import { uuidv4 } from "../util/random"
import { withSessionContext } from "../util/tracing/context"
import { wrapActiveSpan } from "../util/tracing/spans"
import { withSessionContext } from "../util/open-telemetry/context"
import { wrapActiveSpan } from "../util/open-telemetry/spans"

export interface RunOutput {
argv: any
Expand Down
4 changes: 2 additions & 2 deletions core/src/cli/command-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ import {
renderCommands,
} from "./helpers"
import type { GlobalOptions, ParameterValues } from "./params"
import { bindActiveContext, withSessionContext } from "../util/tracing/context"
import { wrapActiveSpan } from "../util/tracing/spans"
import { bindActiveContext, withSessionContext } from "../util/open-telemetry/context"
import { wrapActiveSpan } from "../util/open-telemetry/spans"

const defaultMessageDuration = 3000
const commandLinePrefix = chalk.yellow("🌼 > ")
Expand Down
4 changes: 2 additions & 2 deletions core/src/commands/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ import { GraphResultMapWithoutTask, GraphResultWithoutTask, GraphResults } from
import { splitFirst } from "../util/string"
import { ActionMode } from "../actions/types"
import { AnalyticsHandler } from "../analytics/analytics"
import { withSessionContext } from "../util/tracing/context"
import { wrapActiveSpan } from "../util/tracing/spans"
import { withSessionContext } from "../util/open-telemetry/context"
import { wrapActiveSpan } from "../util/open-telemetry/spans"

export interface CommandConstructor {
new (parent?: CommandGroup): Command
Expand Down
2 changes: 1 addition & 1 deletion core/src/commands/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { removeSlice } from "../util/util"
import { join } from "path"
import { getBuiltinCommands } from "./commands"
import { Log } from "../logger/log-entry"
import { getTracePropagationEnvVars } from "../util/tracing/propagation"
import { getTracePropagationEnvVars } from "../util/open-telemetry/propagation"

function convertArgSpec(spec: CustomCommandOption) {
const params = {
Expand Down
2 changes: 1 addition & 1 deletion core/src/commands/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import moment from "moment"
import { dedent } from "../util/string"
import Spinner from "ink-spinner"
import type { Log } from "../logger/log-entry"
import { bindActiveContext } from "../util/tracing/context"
import { bindActiveContext } from "../util/open-telemetry/context"

const devCommandArgs = {
...serveArgs,
Expand Down
3 changes: 2 additions & 1 deletion core/src/config/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { Schema, z } from "zod"
import { Schema, z, infer as inferZodType, ZodType } from "zod"
import { envVarRegex, identifierRegex, joiIdentifierDescription, userIdentifierRegex } from "./constants"

// Add metadata support to schemas. See https://github.com/colinhacks/zod/issues/273#issuecomment-1434077058
Expand Down Expand Up @@ -76,6 +76,7 @@ type GardenSchema = typeof z & {

// This should be imported instead of z because we augment zod with custom methods
export const s = z as GardenSchema
export type inferType<T extends ZodType<any, any, any>> = inferZodType<T>

s.envVars = () => s.record(s.string().regex(envVarRegex).min(1), z.string())

Expand Down
2 changes: 1 addition & 1 deletion core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,5 @@ export const gardenEnv = {
GARDEN_HARD_CONCURRENCY_LIMIT: env.get("GARDEN_HARD_CONCURRENCY_LIMIT").required(false).default(50).asInt(),
GARDEN_WORKFLOW_RUN_UID: env.get("GARDEN_WORKFLOW_RUN_UID").required(false).asString(),
GARDEN_CLOUD_DOMAIN: env.get("GARDEN_CLOUD_DOMAIN").required(false).asUrlString(),
GARDEN_ENABLE_TRACING: env.get("GARDEN_ENABLE_TRACING").required(false).asBool(),
GARDEN_ENABLE_TRACING: env.get("GARDEN_ENABLE_TRACING").required(false).default("true").asBool(),
}
9 changes: 9 additions & 0 deletions core/src/docs/json-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export class JsonKeyDescription<T = any> extends BaseKeyDescription<T> {
schema = schema.oneOf[0]
}

// FIXME: We only use the first type if there are multiple possible schemas
if (schema.anyOf) {
schema = schema.anyOf[0]
}

this.schema = schema
this.type = getType(schema)

Expand Down Expand Up @@ -119,6 +124,10 @@ export class JsonKeyDescription<T = any> extends BaseKeyDescription<T> {
itemsSchema = itemsSchema.oneOf[0]
}

if (itemsSchema.anyOf) {
itemsSchema = itemsSchema.anyOf[0]
}

return [
new JsonKeyDescription({
schema: itemsSchema,
Expand Down
23 changes: 21 additions & 2 deletions core/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,10 @@ import { MonitorManager } from "./monitors/manager"
import { AnalyticsHandler } from "./analytics/analytics"
import { getGardenInstanceKey } from "./server/helpers"
import { SuggestedCommand } from "./commands/base"
import { OtelTraced } from "./util/tracing/decorators"
import { wrapActiveSpan } from "./util/tracing/spans"
import { OtelTraced } from "./util/open-telemetry/decorators"
import { wrapActiveSpan } from "./util/open-telemetry/spans"
import { GitRepoHandler } from "./vcs/git-repo"
import { configureNoOpExporter } from "./util/open-telemetry/tracing"

const defaultLocalAddress = "localhost"

Expand Down Expand Up @@ -397,6 +398,24 @@ export class Garden {
this.version = getPackageVersion()
this.monitors = params.monitors || new MonitorManager(this.log, this.events)
this.solver = new GraphSolver(this)

// In order not to leak memory, we should ensure that there's always a collector for the OTEL data
// Here we check if the otel-collector was configured and we set a NoOp exporter if it was not
// This is of course not entirely ideal since this puts into this class some level of coupling
// with the plugin based otel-collector.
// Since we don't have the ability to hook into the post provider init stage from within the provider plugin
// especially because it's the absence of said provider that needs to trigger this case,
// there isn't really a cleaner way around this for now.
const providerConfigs = this.getRawProviderConfigs()

const hasOtelCollectorProvider = providerConfigs.some((providerConfig) => {
return providerConfig.name === "otel-collector"
})

if (!hasOtelCollectorProvider) {
this.log.silly("No OTEL collector configured, setting no-op exporter")
configureNoOpExporter()
}
}

static async factory<T extends typeof Garden>(
Expand Down
2 changes: 1 addition & 1 deletion core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { initTracing } from "./util/tracing/tracing"
import { initTracing } from "./util/open-telemetry/tracing"
initTracing()

export { Garden } from "./garden"
3 changes: 2 additions & 1 deletion core/src/plugin/handlers/Build/get-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export const buildResultSchema = createSchema({
}),
})

export interface BuildStatus<T extends BuildAction = BuildAction, D extends {} = BuildResult> extends ActionStatus<T, D> {}
export interface BuildStatus<T extends BuildAction = BuildAction, D extends {} = BuildResult>
extends ActionStatus<T, D> {}

export interface BuildStatusMap extends ActionStatusMap<BuildAction> {
[key: string]: BuildStatus
Expand Down
2 changes: 1 addition & 1 deletion core/src/plugins/exec/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { convertCommandSpec, execRunCommand, getDefaultEnvVars } from "./common"
import { isRunning, killRecursive } from "../../process"
import { sdk } from "../../plugin/sdk"
import { execProvider } from "./exec"
import { getTracePropagationEnvVars } from "../../util/tracing/propagation"
import { getTracePropagationEnvVars } from "../../util/open-telemetry/propagation"
import { DeployState } from "../../types/service"

const persistentLocalProcRetryIntervalMs = 2500
Expand Down
9 changes: 8 additions & 1 deletion core/src/plugins/kubernetes/container/build/buildkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import AsyncLock from "async-lock"
import chalk from "chalk"
import split2 = require("split2")
import { isEmpty } from "lodash"
import { buildSyncVolumeName, buildkitContainerName, buildkitDeploymentName, buildkitImageName, buildkitRootlessImageName, dockerAuthSecretKey } from "../../constants"
import {
buildSyncVolumeName,
buildkitContainerName,
buildkitDeploymentName,
buildkitImageName,
buildkitRootlessImageName,
dockerAuthSecretKey,
} from "../../constants"
import { KubeApi } from "../../api"
import { KubernetesDeployment } from "../../types"
import { Log } from "../../../../logger/log-entry"
Expand Down
74 changes: 74 additions & 0 deletions core/src/plugins/otel-collector/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2018-2023 Garden Technologies, Inc. <[email protected]>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { mergeWith } from "lodash"
import { MergeDeep } from "type-fest"
import {
OtlpHttpExporterConfigPartial,
OtelCollectorOtlpHttpConfiguration,
makeOtlpHttpPartialConfig,
} from "./config/otlphttp"
import {
DatadogExporterConfigPartial,
OtelCollectorDatadogConfiguration,
makeDatadogPartialConfig,
} from "./config/datadog"
import { OtelCollectorNewRelicConfiguration, makeNewRelicPartialConfig } from "./config/newrelic"
import { OtelCollectorBaseConfig, getOtelCollectorBaseConfig } from "./config/base"
import { OtelCollectorHoneycombConfiguration, makeHoneycombPartialConfig } from "./config/honeycomb"
import { OtelCollectorLoggingConfiguration, makeLoggingPartialConfig } from "./config/logging"

export type OtelConfigFile = MergeDeep<
OtelCollectorBaseConfig,
MergeDeep<OtlpHttpExporterConfigPartial, DatadogExporterConfigPartial, { arrayMergeMode: "spread" }>,
{ arrayMergeMode: "spread" }
>

export type OtelExportersConfig =
| OtelCollectorLoggingConfiguration
| OtelCollectorDatadogConfiguration
| OtelCollectorNewRelicConfiguration
| OtelCollectorOtlpHttpConfiguration
| OtelCollectorHoneycombConfiguration

export type OtelCollectorConfigFileOptions = {
exporters: OtelExportersConfig[]
}

function mergeArrays(objValue, srcValue) {
if (Array.isArray(objValue)) {
return objValue.concat(srcValue)
}
return undefined
}

export function getOtelCollectorConfigFile({ exporters }: OtelCollectorConfigFileOptions) {
let config: OtelConfigFile = getOtelCollectorBaseConfig()

for (const exporter of exporters) {
if (exporter.enabled) {
if (exporter.name === "datadog") {
config = mergeWith(config, makeDatadogPartialConfig(exporter), mergeArrays)
}
if (exporter.name === "newrelic") {
config = mergeWith(config, makeNewRelicPartialConfig(exporter), mergeArrays)
}
if (exporter.name === "otlphttp") {
config = mergeWith(config, makeOtlpHttpPartialConfig(exporter), mergeArrays)
}
if (exporter.name === "honeycomb") {
config = mergeWith(config, makeHoneycombPartialConfig(exporter), mergeArrays)
}
if (exporter.name === "logging") {
config = mergeWith(config, makeLoggingPartialConfig(exporter), mergeArrays)
}
}
}

return config
}
71 changes: 71 additions & 0 deletions core/src/plugins/otel-collector/config/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2018-2023 Garden Technologies, Inc. <[email protected]>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { sdk } from "../../../plugin/sdk"

const s = sdk.schema

export const baseValidator = s.object({
name: s.string(),
enabled: s.boolean(),
})

export type OtelCollectorBaseConfig = {
processors: {
batch: null | {
send_batch_max_size?: number
timeout?: string
}
}
exporters: {}
extensions: Record<string, null>
service: {
extensions: string[]
pipelines: {
traces: {
receivers: ["otlp"]
processors: ["batch"]
exporters: string[]
}
}
telemetry: {
logs: {
level: string
}
}
}
}

export function getOtelCollectorBaseConfig(): OtelCollectorBaseConfig {
return {
processors: {
batch: null,
},
exporters: {},
extensions: {
health_check: null,
pprof: null,
zpages: null,
},
service: {
extensions: [],
pipelines: {
traces: {
receivers: ["otlp"],
processors: ["batch"],
exporters: [],
},
},
telemetry: {
logs: {
level: "debug",
},
},
},
}
}
Loading