123 lines
4.5 KiB
TypeScript
123 lines
4.5 KiB
TypeScript
import * as NodeSdk from "@effect/opentelemetry/NodeSdk"
|
|
import { assert, describe, it } from "@effect/vitest"
|
|
import { SeverityNumber } from "@opentelemetry/api-logs"
|
|
import { InMemoryLogRecordExporter, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs"
|
|
import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"
|
|
import * as Clock from "effect/Clock"
|
|
import * as Effect from "effect/Effect"
|
|
import * as Layer from "effect/Layer"
|
|
import * as References from "effect/References"
|
|
|
|
describe("Logger", () => {
|
|
describe("provided", () => {
|
|
const exporter = new InMemoryLogRecordExporter()
|
|
|
|
const TracingLive = NodeSdk.layer(Effect.sync(() => ({
|
|
resource: {
|
|
serviceName: "test"
|
|
},
|
|
logRecordProcessor: [new SimpleLogRecordProcessor({ exporter })]
|
|
})))
|
|
|
|
it.effect("emits log records", () =>
|
|
Effect.gen(function*() {
|
|
yield* Effect.log("test").pipe(
|
|
Effect.repeat({ times: 9 })
|
|
)
|
|
assert.lengthOf(exporter.getFinishedLogRecords(), 10)
|
|
}).pipe(Effect.provide(TracingLive)))
|
|
|
|
it.effect("maps Effect LogLevel to OTel SeverityNumber spec values", () => {
|
|
const severityExporter = new InMemoryLogRecordExporter()
|
|
const SeverityLayer = NodeSdk.layer(Effect.sync(() => ({
|
|
resource: { serviceName: "test" },
|
|
logRecordProcessor: [new SimpleLogRecordProcessor({ exporter: severityExporter })]
|
|
})))
|
|
|
|
return Effect.gen(function*() {
|
|
yield* Effect.logTrace("trace")
|
|
yield* Effect.logDebug("debug")
|
|
yield* Effect.logInfo("info")
|
|
yield* Effect.logWarning("warn")
|
|
yield* Effect.logError("error")
|
|
yield* Effect.logFatal("fatal")
|
|
|
|
const records = severityExporter.getFinishedLogRecords()
|
|
const byText = Object.fromEntries(records.map((r) => [r.severityText, r.severityNumber]))
|
|
|
|
assert.strictEqual(byText.Trace, SeverityNumber.TRACE)
|
|
assert.strictEqual(byText.Debug, SeverityNumber.DEBUG)
|
|
assert.strictEqual(byText.Info, SeverityNumber.INFO)
|
|
assert.strictEqual(byText.Warn, SeverityNumber.WARN)
|
|
assert.strictEqual(byText.Error, SeverityNumber.ERROR)
|
|
assert.strictEqual(byText.Fatal, SeverityNumber.FATAL)
|
|
}).pipe(
|
|
Effect.provide(SeverityLayer.pipe(Layer.provideMerge(Layer.succeed(References.MinimumLogLevel, "Trace"))))
|
|
)
|
|
})
|
|
|
|
it.effect("uses monotonic clock timestamps and keeps them aligned with spans", () => {
|
|
const logExporter = new InMemoryLogRecordExporter()
|
|
const spanExporter = new InMemorySpanExporter()
|
|
const timeNanos = 1_735_689_600_123_456_789n
|
|
const expectedTime: readonly [number, number] = [
|
|
Number(timeNanos / BigInt(1_000_000_000)),
|
|
Number(timeNanos % BigInt(1_000_000_000))
|
|
]
|
|
const skewedClock: Clock.Clock = {
|
|
currentTimeMillisUnsafe: () => 1,
|
|
currentTimeMillis: Effect.succeed(1),
|
|
currentTimeNanosUnsafe: () => timeNanos,
|
|
currentTimeNanos: Effect.succeed(timeNanos),
|
|
sleep: () => Effect.void
|
|
}
|
|
|
|
const TracingLive = NodeSdk.layer(Effect.sync(() => ({
|
|
resource: {
|
|
serviceName: "test"
|
|
},
|
|
spanProcessor: [new SimpleSpanProcessor(spanExporter)],
|
|
logRecordProcessor: [new SimpleLogRecordProcessor({ exporter: logExporter })]
|
|
})))
|
|
|
|
return Effect.gen(function*() {
|
|
yield* Effect.log("test").pipe(
|
|
Effect.withSpan("parent")
|
|
)
|
|
|
|
const logs = logExporter.getFinishedLogRecords()
|
|
const spans = spanExporter.getFinishedSpans()
|
|
assert.lengthOf(logs, 1)
|
|
assert.lengthOf(spans, 1)
|
|
const log = logs[0]!
|
|
const span = spans[0]!
|
|
|
|
assert.deepStrictEqual(log.hrTime, expectedTime)
|
|
assert.deepStrictEqual(log.hrTimeObserved, expectedTime)
|
|
assert.deepStrictEqual(log.hrTime, span.startTime)
|
|
assert.strictEqual(log.attributes.spanId, span.spanContext().spanId)
|
|
assert.strictEqual(log.attributes.traceId, span.spanContext().traceId)
|
|
}).pipe(
|
|
Effect.provide(TracingLive),
|
|
Effect.provideService(Clock.Clock, skewedClock)
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("not provided", () => {
|
|
const exporter = new InMemoryLogRecordExporter()
|
|
|
|
const TracingLive = NodeSdk.layer(Effect.sync(() => ({
|
|
resource: {
|
|
serviceName: "test"
|
|
}
|
|
})))
|
|
|
|
it.effect("withSpan", () =>
|
|
Effect.gen(function*() {
|
|
yield* Effect.log("test")
|
|
assert.lengthOf(exporter.getFinishedLogRecords(), 0)
|
|
}).pipe(Effect.provide(TracingLive)))
|
|
})
|
|
})
|