Skip to content

Commit

Permalink
MR changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jpowersdev committed Feb 4, 2025
1 parent 67ebeef commit 2988167
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 86 deletions.
36 changes: 29 additions & 7 deletions packages/opentelemetry/src/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* @since 1.0.0
*/
import type * as Otel from "@opentelemetry/sdk-logs"
import * as Otel from "@opentelemetry/sdk-logs"
import type { NonEmptyReadonlyArray } from "effect/Array"
import type { Tag } from "effect/Context"
import type { Effect } from "effect/Effect"
import type { Layer } from "effect/Layer"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import * as Logger from "effect/Logger"
import * as internal from "./internal/logger.js"
import type { Resource } from "./Resource.js"
import { Resource } from "./Resource.js"

/**
* @since 1.0.0
Expand All @@ -28,15 +28,15 @@ export const OtelLoggerProvider: Tag<OtelLoggerProvider, Otel.LoggerProvider> =
* @since 1.0.0
* @category layers
*/
export const layerLogger: Layer<never, never, OtelLoggerProvider> = Logger.addEffect(
export const layerLogger: Layer.Layer<never, never, OtelLoggerProvider> = Logger.addEffect(
internal.make
)

/**
* @since 1.0.0
* @category constructors
*/
export const logger: Effect<Logger.Logger<unknown, void>, never, OtelLoggerProvider> = internal.make
export const logger: Effect.Effect<Logger.Logger<unknown, void>, never, OtelLoggerProvider> = internal.make

/**
* @since 1.0.0
Expand All @@ -45,4 +45,26 @@ export const logger: Effect<Logger.Logger<unknown, void>, never, OtelLoggerProvi
export const layerLoggerProvider = (
processor: Otel.LogRecordProcessor | NonEmptyReadonlyArray<Otel.LogRecordProcessor>,
config?: Omit<Otel.LoggerProviderConfig, "resource">
): Layer<OtelLoggerProvider, never, Resource> => internal.layerLoggerProvider(processor, config)
) =>
Layer.scoped(
internal.LoggerProvider,
Effect.flatMap(
Resource,
(resource) =>
Effect.acquireRelease(
Effect.sync(() => {
const provider = new Otel.LoggerProvider({
...(config ?? undefined),
resource
})
if (Array.isArray(processor)) {
processor.forEach((p) => provider.addLogRecordProcessor(p))
} else {
provider.addLogRecordProcessor(processor as any)
}
return provider
}),
(provider) => Effect.ignoreLogged(Effect.promise(() => provider.forceFlush().then(() => provider.shutdown())))
)
)
)
68 changes: 11 additions & 57 deletions packages/opentelemetry/src/internal/logger.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import * as Otel from "@opentelemetry/sdk-logs"
import type { NonEmptyReadonlyArray } from "effect/Array"
import type * as Otel from "@opentelemetry/sdk-logs"
import * as Clock from "effect/Clock"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Inspectable from "effect/Inspectable"
import * as Layer from "effect/Layer"
import * as Logger from "effect/Logger"
import * as Match from "effect/Match"
import * as Predicate from "effect/Predicate"
import * as Record from "effect/Record"
import * as FiberRefs from "../../../effect/src/FiberRefs.js"
import * as defaultServices from "../../../effect/src/internal/defaultServices.js"
import type { OtelLoggerProvider } from "../Logger.js"
import { Resource } from "../Resource.js"
import { recordToAttributes, unknownToAttributeValue } from "./utils.js"

/** @internal */
export const LoggerProvider = Context.GenericTag<
Expand All @@ -19,55 +17,29 @@ export const LoggerProvider = Context.GenericTag<
"@effect/opentelemetry/Logger/OtelLoggerProvider"
)

/** @internal */
export const layerLoggerProvider = (
processor: Otel.LogRecordProcessor | NonEmptyReadonlyArray<Otel.LogRecordProcessor>,
config?: Omit<Otel.LoggerProviderConfig, "resource">
) =>
Layer.scoped(
LoggerProvider,
Effect.flatMap(
Resource,
(resource) =>
Effect.acquireRelease(
Effect.sync(() => {
const provider = new Otel.LoggerProvider({
...(config ?? undefined),
resource
})
if (Array.isArray(processor)) {
processor.forEach((p) => provider.addLogRecordProcessor(p))
} else {
provider.addLogRecordProcessor(processor as any)
}
return provider
}),
(provider) => Effect.ignoreLogged(Effect.promise(() => provider.forceFlush().then(() => provider.shutdown())))
)
)
)

/** @internal */
export const make = Effect.gen(function*() {
const loggerProvider = yield* LoggerProvider

const otelLogger = loggerProvider.getLogger("@effect/opentelemetry")

return Logger.make((options) => {
const services = FiberRefs.getOrDefault(options.context, defaultServices.currentServices)
const clock = Context.get(services, Clock.Clock)
const structured = Logger.structuredLogger.log(options)

const attributes = {
fiberId: structured.fiberId,
...formatLogSpans(structured.spans),
...formatAnnotations(structured.annotations)
...recordToAttributes(structured.annotations),
...formatLogSpans(structured.spans)
}

otelLogger.emit({
body: formatMessage(structured.message),
body: unknownToAttributeValue(structured.message),
severityText: structured.logLevel,
severityNumber: options.logLevel.ordinal,
timestamp: options.date,
observedTimestamp: new Date(),
observedTimestamp: clock.unsafeCurrentTimeMillis(),
attributes
})
})
Expand All @@ -77,21 +49,3 @@ export const make = Effect.gen(function*() {
const formatLogSpans = Record.mapEntries(
(value, key) => [`logSpan.${key}`, `${value}ms`] as const
)

/** @internal */
const formatAnnotations = Record.map(
(value) => Predicate.isString(value) ? value : Inspectable.format(value)
)

/** @internal */
const formatMessage = Match.type<unknown>().pipe(
Match.whenOr(
Predicate.isString,
Predicate.isNumber,
Predicate.isBoolean,
Predicate.isUndefined,
Predicate.isNull,
(value) => value
),
Match.orElse((_) => Inspectable.format(_))
)
23 changes: 1 addition & 22 deletions packages/opentelemetry/src/internal/tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import type { Exit } from "effect/Exit"
import { dual } from "effect/Function"
import * as Inspectable from "effect/Inspectable"
import * as Layer from "effect/Layer"
import * as Option from "effect/Option"
import * as EffectTracer from "effect/Tracer"
import { Resource } from "../Resource.js"
import type { OtelTraceFlags, OtelTracer, OtelTracerProvider, OtelTraceState } from "../Tracer.js"
import { nanosToHrTime, recordToAttributes, unknownToAttributeValue } from "./utils.js"

const OtelSpanTypeId = Symbol.for("@effect/opentelemetry/Tracer/OtelSpan")

Expand Down Expand Up @@ -257,11 +257,6 @@ export const layer = layerWithoutOtelTracer.pipe(
// utils
// -------------------------------------------------------------------------------------

const bigint1e9 = 1_000_000_000n
const nanosToHrTime = (timestamp: bigint): OtelApi.HrTime => {
return [Number(timestamp / bigint1e9), Number(timestamp % bigint1e9)]
}

const createTraceState = Option.liftThrowable(OtelApi.createTraceState)

const populateContext = (
Expand Down Expand Up @@ -300,22 +295,6 @@ const extractTraceTag = <I, S>(
() => Context.getOption(parent.context, tag)
)

const recordToAttributes = (value: Record<string, unknown>): OtelApi.Attributes => {
return Object.entries(value).reduce((acc, [key, value]) => {
acc[key] = unknownToAttributeValue(value)
return acc
}, {} as OtelApi.Attributes)
}

const unknownToAttributeValue = (value: unknown): OtelApi.AttributeValue => {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return value
} else if (typeof value === "bigint") {
return Number(value)
}
return Inspectable.toStringUnknown(value)
}

/** @internal */
export const withSpanContext = dual<
(
Expand Down
23 changes: 23 additions & 0 deletions packages/opentelemetry/src/internal/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type * as OtelApi from "@opentelemetry/api"
import * as Inspectable from "effect/Inspectable"

const bigint1e9 = 1_000_000_000n
export const nanosToHrTime = (timestamp: bigint): OtelApi.HrTime => {
return [Number(timestamp / bigint1e9), Number(timestamp % bigint1e9)]
}

export const recordToAttributes = (value: Record<string, unknown>): OtelApi.Attributes => {
return Object.entries(value).reduce((acc, [key, value]) => {
acc[key] = unknownToAttributeValue(value)
return acc
}, {} as OtelApi.Attributes)
}

export const unknownToAttributeValue = (value: unknown): OtelApi.AttributeValue => {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return value
} else if (typeof value === "bigint") {
return Number(value)
}
return Inspectable.toStringUnknown(value)
}

0 comments on commit 2988167

Please sign in to comment.