///|
/// Full OpenTelemetry severity ladder used by `LogRecord`.
///
/// The 24 slots let bridges preserve finer-grained severity from existing
/// logging frameworks.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
pub(all) enum SeverityNumber {
Trace
Trace2
Trace3
Trace4
Debug
Debug2
Debug3
Debug4
Info
Info2
Info3
Info4
Warn
Warn2
Warn3
Warn4
Error
Error2
Error3
Error4
Fatal
Fatal2
Fatal3
Fatal4
} derive(Eq, Compare, Hash, ToJson, Debug)
///|
/// Structured log value preserved by the SDK and OTLP exporter.
///
/// Use `Map` and `Array` when the backend should receive structured payloads
/// rather than preformatted strings.
/// Spec: https://opentelemetry.io/docs/specs/otel/common/#anyvalue
pub(all) enum AnyValue {
Bool(Bool)
Int64(Int64)
Double(Double)
String(String)
Bytes(Bytes)
Array(Array[AnyValue])
Map(Array[KeyValue])
} derive(Eq, ToJson, Debug)
///|
/// Structured log attribute key/value pair.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub struct KeyValue {
key : @common.Key
value : AnyValue
} derive(Eq, ToJson, Debug)
///|
/// Immutable log payload exported by processors and exporters.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#additional-logrecord-interfaces
pub struct LogRecord {
event_name : String?
target : String?
body : AnyValue
timestamp_unix_nano : Int64
observed_timestamp_unix_nano : Int64
severity_number : SeverityNumber?
severity_text : String?
attributes : Array[KeyValue]
trace_context : @common.SpanContext?
resource : @resource.Resource
instrumentation_scope : @common.InstrumentationScope
} derive(Eq, ToJson, Debug)
///|
/// Exporter callback wrapper for log batches.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub struct LogExporter {
export_fn : async (ArrayView[LogRecord]) -> @error.OTelSdkResult
force_flush_fn : async () -> @error.OTelSdkResult
shutdown_fn : async () -> @error.OTelSdkResult
name_fn : () -> String
}
///|
/// Processing hook interface for logs.
///
/// Processors receive immutable log snapshots after the logger attaches
/// resource and instrumentation scope metadata.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor
pub struct LogProcessor {
emit_fn : async (LogRecord) -> Unit
force_flush_fn : async () -> @error.OTelSdkResult
shutdown_fn : async () -> @error.OTelSdkResult
}
///|
/// Processor that exports each log record immediately.
///
/// Useful for tests and local debugging. Network exporters should usually be
/// wrapped by `BatchLogProcessor`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#simple-processor
pub struct SimpleLogProcessor {
inner : LogProcessor
}
///|
/// Processor that buffers log records and exports them in batches.
///
/// The background loop is not started automatically. Use global/facade
/// `spawn_background_tasks()` after provider registration.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor
pub struct BatchLogProcessor {
inner : LogProcessor
state : Ref[BatchProcessorState]
}
///|
/// Configuration for batch log processing.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor
pub struct BatchConfig {
max_queue_size : Int
max_export_batch_size : Int
scheduled_delay_millis : Int
export_timeout_millis : Int
} derive(Eq, Compare, Hash, ToJson, Debug)
///|
struct InMemoryLogExporterState {
finished_logs : Array[LogRecord]
is_shutdown : Bool
}
///|
/// Test exporter that keeps finished logs in memory.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub struct InMemoryLogExporter {
state : Ref[InMemoryLogExporterState]
}
///|
struct BatchProcessorState {
exporter : LogExporter
config : BatchConfig
queue : Array[LogRecord]
is_shutdown : Bool
}
///|
struct LoggerProviderState {
resource : @resource.Resource
processors : Array[LogProcessor]
batch_processors : Array[BatchLogProcessor]
is_shutdown : Bool
}
///|
/// Owns resource metadata and the processor pipeline for loggers.
///
/// Applications create one provider, configure processors/exporters, register it
/// if needed, then shut it down during application teardown.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub struct SdkLoggerProvider {
state : Ref[LoggerProviderState]
}
///|
/// Builder for `SdkLoggerProvider`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub struct SdkLoggerProviderBuilder {
mut resource : @resource.Resource
processors : Array[LogProcessor]
batch_processors : Array[BatchLogProcessor]
}
///|
/// Logger scoped to one instrumentation library.
///
/// The instrumentation scope is copied onto every emitted log record.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logger
pub struct SdkLogger {
provider : SdkLoggerProvider
instrumentation_scope : @common.InstrumentationScope
}
///|
/// Creates a structured log attribute.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub fn KeyValue::new(key : StringView, value : AnyValue) -> KeyValue {
{ key: @common.Key::new(key), value }
}
///|
/// Converts a shared SDK attribute value into a log-specific structured value.
/// Spec: https://opentelemetry.io/docs/specs/otel/common/#anyvalue
pub fn AnyValue::from_common(value : @common.Value) -> AnyValue {
match value {
Bool(value) => Bool(value)
Int64(value) => Int64(value)
Double(value) => Double(value)
String(value) => String(value)
Bytes(value) => Bytes(value)
Array(values) => {
let converted = []
for value in values {
converted.push(AnyValue::from_common(value))
}
Array(converted)
}
}
}
///|
/// Converts a shared SDK attribute into a log-specific structured attribute.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub fn KeyValue::from_common(attribute : @common.KeyValue) -> KeyValue {
{ key: attribute.key, value: AnyValue::from_common(attribute.value) }
}
///|
/// Creates a log record snapshot.
///
/// This is the immutable SDK-side representation. Public API users usually
/// build `interface/logs.LogRecord` values and let `Logger::emit()` convert
/// them into this type.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#additional-logrecord-interfaces
pub fn LogRecord::new(
body : AnyValue,
timestamp_unix_nano? : Int64 = @utils.now_unix_nano(),
observed_timestamp_unix_nano? : Int64 = @utils.now_unix_nano(),
severity_number? : SeverityNumber? = None,
severity_text? : String? = None,
event_name? : String? = None,
target? : String? = None,
attributes? : ArrayView[KeyValue] = [],
trace_context? : @common.SpanContext? = None,
resource? : @resource.Resource = @resource.Resource::empty(),
instrumentation_scope? : @common.InstrumentationScope = Default::default(),
) -> LogRecord {
{
event_name,
target,
body,
timestamp_unix_nano,
observed_timestamp_unix_nano,
severity_number,
severity_text,
attributes: attributes.to_owned(),
trace_context,
resource,
instrumentation_scope,
}
}
///|
fn parse_env_int(key : StringView) -> Int? {
match @utils.getenv(key) {
Some(value) => {
let value = value.trim()
if value == "" {
None
} else {
Some(@string.parse_int(value.to_owned()) catch { _ => return None })
}
}
None => None
}
}
///|
fn env_or(key : StringView, fallback : Int) -> Int {
match parse_env_int(key) {
Some(value) => value
None => fallback
}
}
///|
/// Creates normalized batch log processor configuration.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor
pub fn BatchConfig::new(
max_queue_size? : Int = 2048,
max_export_batch_size? : Int = 512,
scheduled_delay_millis? : Int = 1000,
export_timeout_millis? : Int = 30000,
) -> BatchConfig {
let max_queue_size = if max_queue_size <= 0 { 1 } else { max_queue_size }
let max_export_batch_size = if max_export_batch_size <= 0 {
1
} else if max_export_batch_size > max_queue_size {
max_queue_size
} else {
max_export_batch_size
}
let scheduled_delay_millis = if scheduled_delay_millis <= 0 {
1
} else {
scheduled_delay_millis
}
let export_timeout_millis = if export_timeout_millis <= 0 {
1
} else {
export_timeout_millis
}
{
max_queue_size,
max_export_batch_size,
scheduled_delay_millis,
export_timeout_millis,
}
}
///|
pub impl Default for BatchConfig with fn default() -> BatchConfig {
BatchConfig::new(
max_queue_size=env_or("OTEL_BLRP_MAX_QUEUE_SIZE", 2048),
max_export_batch_size=env_or("OTEL_BLRP_MAX_EXPORT_BATCH_SIZE", 512),
scheduled_delay_millis=env_or("OTEL_BLRP_SCHEDULE_DELAY", 1000),
export_timeout_millis=env_or("OTEL_BLRP_EXPORT_TIMEOUT", 30000),
)
}
///|
/// Builds a log exporter from callbacks.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub fn LogExporter::new(
export_fn : async (ArrayView[LogRecord]) -> @error.OTelSdkResult,
force_flush_fn? : async () -> @error.OTelSdkResult = () => @error.ok(),
shutdown_fn? : async () -> @error.OTelSdkResult = () => @error.ok(),
name_fn? : () -> String = () => "custom",
) -> LogExporter {
{ export_fn, force_flush_fn, shutdown_fn, name_fn }
}
///|
/// Exports one batch of log records.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#export
pub async fn LogExporter::export_batch(
self : LogExporter,
batch : ArrayView[LogRecord],
) -> @error.OTelSdkResult {
(self.export_fn)(batch)
}
///|
/// Requests exporter flush.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#forceflush
pub async fn LogExporter::force_flush(
self : LogExporter,
) -> @error.OTelSdkResult {
(self.force_flush_fn)()
}
///|
/// Requests exporter shutdown with an optional timeout budget in milliseconds.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#shutdown
pub async fn LogExporter::shutdown_with_timeout(
self : LogExporter,
timeout_millis : Int,
) -> @error.OTelSdkResult {
ignore(timeout_millis)
(self.shutdown_fn)()
}
///|
/// Requests exporter shutdown.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#shutdown
pub async fn LogExporter::shutdown(self : LogExporter) -> @error.OTelSdkResult {
self.shutdown_with_timeout(5000)
}
///|
/// Returns a human-readable exporter name.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub fn LogExporter::name(self : LogExporter) -> String {
(self.name_fn)()
}
///|
/// Builds a log processor from callbacks.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor
pub fn LogProcessor::new(
emit_fn? : async (LogRecord) -> Unit = _ => (),
force_flush_fn? : async () -> @error.OTelSdkResult = () => @error.ok(),
shutdown_fn? : async () -> @error.OTelSdkResult = () => @error.ok(),
) -> LogProcessor {
{ emit_fn, force_flush_fn, shutdown_fn }
}
///|
/// Processes one log record.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor
pub async fn LogProcessor::emit(
self : LogProcessor,
record : LogRecord,
) -> Unit {
(self.emit_fn)(record)
}
///|
/// Requests processor flush.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor
pub async fn LogProcessor::force_flush(
self : LogProcessor,
) -> @error.OTelSdkResult {
(self.force_flush_fn)()
}
///|
/// Requests processor shutdown with an optional timeout budget in milliseconds.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor
pub async fn LogProcessor::shutdown_with_timeout(
self : LogProcessor,
timeout_millis : Int,
) -> @error.OTelSdkResult {
ignore(timeout_millis)
(self.shutdown_fn)()
}
///|
/// Requests processor shutdown.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor
pub async fn LogProcessor::shutdown(
self : LogProcessor,
) -> @error.OTelSdkResult {
self.shutdown_with_timeout(5000)
}
///|
/// Creates an empty in-memory log exporter.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub fn InMemoryLogExporter::new() -> InMemoryLogExporter {
{ state: Ref({ finished_logs: [], is_shutdown: false }) }
}
///|
/// Returns the finished log records accumulated so far.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub fn InMemoryLogExporter::finished_logs(
self : InMemoryLogExporter,
) -> Array[LogRecord] {
self.state.val.finished_logs.copy()
}
///|
/// Clears the in-memory log buffer.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter
pub fn InMemoryLogExporter::reset(self : InMemoryLogExporter) -> Unit {
self.state.val = { ..self.state.val, finished_logs: [] }
}
///|
/// Erases the concrete in-memory exporter type into `LogExporter`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#export
pub fn InMemoryLogExporter::into_log_exporter(
self : InMemoryLogExporter,
) -> LogExporter {
let state = self.state
LogExporter::new(
(batch : ArrayView[LogRecord]) => {
if state.val.is_shutdown {
return @error.already_shutdown()
}
let finished_logs = state.val.finished_logs.copy()
finished_logs.append(batch)
state.val = { ..state.val, finished_logs, }
@error.ok()
},
shutdown_fn=() => {
if state.val.is_shutdown {
@error.already_shutdown()
} else {
state.val = { ..state.val, is_shutdown: true }
@error.ok()
}
},
name_fn=() => "in_memory",
)
}
///|
/// Creates a simple processor that exports each emitted record immediately.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#simple-processor
pub fn SimpleLogProcessor::new(exporter : LogExporter) -> SimpleLogProcessor {
{
inner: LogProcessor::new(
emit_fn=(record : LogRecord) => ignore(exporter.export_batch([record])),
force_flush_fn=() => exporter.force_flush(),
shutdown_fn=() => exporter.shutdown(),
),
}
}
///|
/// Erases the concrete simple processor type into `LogProcessor`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#simple-processor
pub fn SimpleLogProcessor::into_log_processor(
self : SimpleLogProcessor,
) -> LogProcessor {
self.inner
}
///|
/// Flushes the in-memory queue to the exporter in batch-sized chunks.
async fn flush_log_queue(
state : Ref[BatchProcessorState],
) -> @error.OTelSdkResult {
if state.val.is_shutdown {
return @error.already_shutdown()
}
while !state.val.queue.is_empty() {
let batch : Array[LogRecord] = []
let batch_size = if state.val.queue.length() <
state.val.config.max_export_batch_size {
state.val.queue.length()
} else {
state.val.config.max_export_batch_size
}
for i = 0; i < batch_size; i = i + 1 {
ignore(i)
batch.push(state.val.queue.remove(0))
}
match state.val.exporter.export_batch(batch) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
@error.ok()
}
///|
/// Creates a batch log processor with a bounded queue.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor
pub fn BatchLogProcessor::new(
exporter : LogExporter,
config? : BatchConfig = Default::default(),
) -> BatchLogProcessor {
let state : Ref[BatchProcessorState] = Ref({
exporter,
config,
queue: [],
is_shutdown: false,
})
{
inner: LogProcessor::new(
emit_fn=(record : LogRecord) => {
if state.val.is_shutdown {
return
}
if state.val.queue.length() >= state.val.config.max_queue_size {
// Keep the newest records under sustained pressure.
ignore(state.val.queue.remove(0))
}
state.val.queue.push(record)
if state.val.queue.length() >= state.val.config.max_export_batch_size {
ignore(flush_log_queue(state))
}
},
force_flush_fn=() => {
match flush_log_queue(state) {
Ok(_) => state.val.exporter.force_flush()
Err(err) => Err(err)
}
},
shutdown_fn=() => {
if state.val.is_shutdown {
return @error.already_shutdown()
}
let result = flush_log_queue(state)
state.val = { ..state.val, is_shutdown: true }
match result {
Ok(_) =>
state.val.exporter.shutdown_with_timeout(
state.val.config.export_timeout_millis,
)
Err(err) => Err(err)
}
},
),
state,
}
}
///|
/// Erases the concrete batch processor type into `LogProcessor`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor
pub fn BatchLogProcessor::into_log_processor(
self : BatchLogProcessor,
) -> LogProcessor {
self.inner
}
///|
/// Background loop for scheduled batch exports.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor
pub async fn BatchLogProcessor::run(self : BatchLogProcessor) -> Unit {
while !self.state.val.is_shutdown {
@async.sleep(self.state.val.config.scheduled_delay_millis)
if self.state.val.is_shutdown {
return
}
ignore(flush_log_queue(self.state))
ignore(self.state.val.exporter.force_flush())
}
}
///|
/// Starts building a logger provider with the default resource.
///
/// Add processors or exporters before calling `build()`. A provider with no
/// processors accepts logs but exports nothing.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProvider::builder() -> SdkLoggerProviderBuilder {
{
resource: @resource.Resource::builder().build(),
processors: [],
batch_processors: [],
}
}
///|
/// Replaces the provider resource.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProviderBuilder::with_resource(
self : SdkLoggerProviderBuilder,
resource : @resource.Resource,
) -> SdkLoggerProviderBuilder {
self.resource = resource
self
}
///|
/// Appends a processor to the logger pipeline.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProviderBuilder::with_log_processor(
self : SdkLoggerProviderBuilder,
processor : LogProcessor,
) -> SdkLoggerProviderBuilder {
self.processors.push(processor)
self
}
///|
/// Appends a simple processor around the exporter.
///
/// Each emitted log is exported from `SdkLogger::emit()`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProviderBuilder::with_simple_exporter(
self : SdkLoggerProviderBuilder,
exporter : LogExporter,
) -> SdkLoggerProviderBuilder {
self.with_log_processor(
SimpleLogProcessor::new(exporter).into_log_processor(),
)
}
///|
/// Appends a batch processor around the exporter and registers it for
/// background execution.
///
/// The processor is registered with the provider, but its export loop only runs
/// after `spawn_batch_processor_tasks()` or the facade/global equivalent is
/// called.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProviderBuilder::with_batch_exporter(
self : SdkLoggerProviderBuilder,
exporter : LogExporter,
config? : BatchConfig = Default::default(),
) -> SdkLoggerProviderBuilder {
let batch_processor = BatchLogProcessor::new(exporter, config~)
self.batch_processors.push(batch_processor)
self.with_log_processor(batch_processor.into_log_processor())
}
///|
/// Builds the logger provider.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProviderBuilder::build(
self : SdkLoggerProviderBuilder,
) -> SdkLoggerProvider {
{
state: Ref({
resource: self.resource,
processors: self.processors.copy(),
batch_processors: self.batch_processors.copy(),
is_shutdown: false,
}),
}
}
///|
/// Returns the provider resource.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProvider::resource(
self : SdkLoggerProvider,
) -> @resource.Resource {
self.state.val.resource
}
///|
/// Spawns background tasks for all configured batch processors.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProvider::spawn_batch_processor_tasks(
self : SdkLoggerProvider,
group : @async.TaskGroup[Unit],
allow_failure? : Bool = false,
) -> Unit {
for batch_processor in self.state.val.batch_processors {
group.spawn_bg(no_wait=true, allow_failure~, () => batch_processor.run())
}
}
///|
/// Creates a logger scoped to the supplied instrumentation library metadata.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logger-creation
pub fn SdkLoggerProvider::logger(
self : SdkLoggerProvider,
name : StringView,
version? : String? = None,
schema_url? : String? = None,
attributes? : ArrayView[@common.KeyValue] = [],
) -> SdkLogger {
let mut scope = @common.InstrumentationScope::builder(name)
match version {
Some(version) => scope = scope.with_version(version)
None => ()
}
match schema_url {
Some(schema_url) => scope = scope.with_schema_url(schema_url)
None => ()
}
scope = scope.with_attributes(attributes)
{ provider: self, instrumentation_scope: scope.build() }
}
///|
/// Flushes every configured processor.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#forceflush
pub async fn SdkLoggerProvider::force_flush(
self : SdkLoggerProvider,
) -> @error.OTelSdkResult {
if self.state.val.is_shutdown {
return @error.already_shutdown()
}
for processor in self.state.val.processors {
match processor.force_flush() {
Ok(_) => ()
Err(err) => return Err(err)
}
}
@error.ok()
}
///|
/// Shuts down every configured processor and marks the provider unusable.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#shutdown
pub async fn SdkLoggerProvider::shutdown_with_timeout(
self : SdkLoggerProvider,
timeout_millis : Int,
) -> @error.OTelSdkResult {
if self.state.val.is_shutdown {
return @error.already_shutdown()
}
let processors = self.state.val.processors.copy()
self.state.val = { ..self.state.val, is_shutdown: true }
for processor in processors {
match processor.shutdown_with_timeout(timeout_millis) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
@error.ok()
}
///|
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#shutdown
pub async fn SdkLoggerProvider::shutdown(
self : SdkLoggerProvider,
) -> @error.OTelSdkResult {
self.shutdown_with_timeout(5000)
}
///|
fn api_sorted_keys(values : Map[String, @api.AnyValue]) -> Array[String] {
let keys = values.keys().to_array()
for i in 1.. 0 && current < keys[j - 1] {
keys[j] = keys[j - 1]
j = j - 1
}
keys[j] = current
}
keys
}
///|
fn any_value_from_api(value : @api.AnyValue) -> AnyValue {
match value {
Int(value) => Int64(value)
Double(value) => Double(value)
String(value) => String(value)
Boolean(value) => Bool(value)
Bytes(value) => Bytes(value)
ListAny(values) => {
let converted = []
for value in values {
converted.push(any_value_from_api(value))
}
Array(converted)
}
Map(values) => {
let converted = []
for key in api_sorted_keys(values) {
guard! values.get(key) is Some(value)
converted.push(KeyValue::new(key, any_value_from_api(value)))
}
Map(converted)
}
}
}
///|
fn key_value_from_api(attribute : @api.KeyValue) -> KeyValue {
{ key: attribute.key, value: any_value_from_api(attribute.value) }
}
///|
fn severity_from_api(severity : @api.Severity?) -> SeverityNumber? {
match severity {
Some(Trace) => Some(Trace)
Some(Trace2) => Some(Trace2)
Some(Trace3) => Some(Trace3)
Some(Trace4) => Some(Trace4)
Some(Debug) => Some(Debug)
Some(Debug2) => Some(Debug2)
Some(Debug3) => Some(Debug3)
Some(Debug4) => Some(Debug4)
Some(Info) => Some(Info)
Some(Info2) => Some(Info2)
Some(Info3) => Some(Info3)
Some(Info4) => Some(Info4)
Some(Warn) => Some(Warn)
Some(Warn2) => Some(Warn2)
Some(Warn3) => Some(Warn3)
Some(Warn4) => Some(Warn4)
Some(Error) => Some(Error)
Some(Error2) => Some(Error2)
Some(Error3) => Some(Error3)
Some(Error4) => Some(Error4)
Some(Fatal) => Some(Fatal)
Some(Fatal2) => Some(Fatal2)
Some(Fatal3) => Some(Fatal3)
Some(Fatal4) => Some(Fatal4)
None => None
}
}
///|
fn context_from_api_trace_context(
span_context : @common.SpanContext?,
) -> @context.Context {
match span_context {
Some(span_context) if span_context.is_remote() =>
@context.Context::empty().with_remote_span_context(span_context)
Some(span_context) =>
@context.Context::empty().with_span_context(span_context)
None => @context.Context::empty()
}
}
///|
fn SdkLogger::into_api_logger(self : SdkLogger) -> @api.Logger {
@api.Logger::from_functions(
record => {
let severity_text = match record.severity_text {
Some(severity_text) => Some(severity_text)
None => record.severity_number.map(severity => severity.name())
}
let attributes = []
for attribute in record.attributes {
attributes.push(key_value_from_api(attribute))
}
self.emit(
match record.body {
Some(body) => any_value_from_api(body)
None => String("")
},
context=context_from_api_trace_context(record.trace_context),
severity_number=severity_from_api(record.severity_number),
severity_text~,
event_name=record.event_name,
target=record.target,
attributes~,
timestamp_unix_nano=match record.timestamp_unix_nano {
Some(timestamp_unix_nano) => timestamp_unix_nano
None => @utils.now_unix_nano()
},
observed_timestamp_unix_nano=match record.observed_timestamp_unix_nano {
Some(observed_timestamp_unix_nano) => observed_timestamp_unix_nano
None => @utils.now_unix_nano()
},
)
},
event_enabled_fn=(_, _, _) => !self.provider.state.val.is_shutdown,
)
}
///|
/// Erases this SDK provider into the public logs API provider.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider
pub fn SdkLoggerProvider::into_logger_provider(
self : SdkLoggerProvider,
) -> @api.LoggerProvider {
@api.LoggerProvider::from_functions(scope => {
self
.logger(
scope.name(),
version=scope.version(),
schema_url=scope.schema_url(),
attributes=scope.attributes(),
)
.into_api_logger()
})
}
///|
/// Extracts a local span context from the supplied context for log correlation.
fn trace_context_from_context(
context : @context.Context,
) -> @common.SpanContext? {
match context.span_context() {
Some(span_context) if span_context.is_valid() =>
Some(span_context.with_remote(false))
_ => None
}
}
///|
/// Emits one log record through the provider pipeline.
///
/// The logger attaches resource and instrumentation scope metadata before
/// forwarding the record to processors.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord
pub async fn SdkLogger::emit(
self : SdkLogger,
body : AnyValue,
context? : @context.Context = Default::default(),
severity_number? : SeverityNumber? = None,
severity_text? : String? = None,
event_name? : String? = None,
target? : String? = None,
attributes? : ArrayView[KeyValue] = [],
timestamp_unix_nano? : Int64 = @utils.now_unix_nano(),
observed_timestamp_unix_nano? : Int64 = @utils.now_unix_nano(),
) -> Unit {
if self.provider.state.val.is_shutdown {
return
}
let record = LogRecord::new(
body,
timestamp_unix_nano~,
observed_timestamp_unix_nano~,
severity_number~,
severity_text~,
event_name~,
target~,
attributes~,
trace_context=trace_context_from_context(context),
resource=self.provider.state.val.resource,
instrumentation_scope=self.instrumentation_scope,
)
for processor in self.provider.state.val.processors {
processor.emit(record)
}
}