purple_logger_otel_sdk 0.1.3
purple_logger_otel_sdk: ^0.1.3 copied to clipboard
PurpleOTel SDK bridge for purple_logger — structured logs flow to OpenTelemetry with trace correlation.
PurpleLogger OTel SDK Bridge
Connect purple_logger to PurpleOTel SDK. Structured logs flow seamlessly into OpenTelemetry with automatic severity mapping and trace correlation.
Features #
- EventLogger integration — Receives fully assembled
LogEventobjects directly from purple_logger's provider pipeline, avoiding double allocation - Severity mapping — PurpleLogLevel → OTel SeverityNumber mapping following the OTel Logs specification
- Trace correlation — LogRecords automatically inherit
traceIdandspanIdfrom active spans when running inside a trace context - Property propagation — All structured properties (scope properties + caller properties) become OTel attributes
- Error capturing — Exceptions and stack traces are attached as
error.type,error.message, anderror.stackTraceattributes - Provider caching — Logger instances are cached by category name, ensuring a single OTel logger per category
Architecture #
┌──────────────────────────────────────────────────────────────────────────┐
│ purple_logger │
│ • LoggerImpl.log() → LogEvent → ProviderLogger.write(event) │
│ • Provides: Logger, LoggerProvider, LoggingScope, FilterRuleSet │
└───────────────────────────────┬──────────────────────────────────────────┘
│ LogEvent
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ purple_logger_otel (abstraction) │
│ • OtelLoggerProvider — abstract provider with logger caching │
│ • OtelLogger — abstract logger with property merging & trace extraction│
│ • OtelSeverityMapping — PurpleLogLevel → OTel severity mixin │
│ • TraceContext — vendor‑neutral traceId/spanId value object │
└───────────────────────────────┬──────────────────────────────────────────┘
│ extends
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ purple_logger_otel_sdk (this package) │
│ • PurpleOtelLoggerProvider — concrete bridge to PurpleOTel SDK │
│ • _PurpleOtelLogger.write(LogEvent) — converts event → OTel LogRecord │
│ • emitToOtel() — calls SDKLogger.emit() with attributes │
└───────────────────────────────┬──────────────────────────────────────────┘
│ OTel LogRecord
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ purple_otel_sdk │
│ • SDKLoggerProvider, SDKLogger — OTel SDK logger implementation │
│ • Processors & Exporters — Simple/Batch + Console/OTLP │
│ • Resource, Attributes, Severity — OTel data model │
└───────────────────────────────┬──────────────────────────────────────────┘
│ OTLP / console
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ OTel Collector / Backend │
│ • Grafana + Tempo + Loki • Azure Monitor / App Insights │
│ • Jaeger • Datadog │
│ • any OTLP-compatible backend │
└──────────────────────────────────────────────────────────────────────────┘
Installation #
dependencies:
purple_logger: ^0.3.0
purple_logger_otel: ^0.1.0
purple_logger_otel_sdk: ^0.1.0
purple_otel_sdk: ^0.1.0
dart pub get
Quick Start #
A complete working example that bridges purple_logger logs into OpenTelemetry:
import 'package:purple_logger/purple_logger.dart';
import 'package:purple_otel_sdk/purple_otel_sdk.dart';
import 'package:purple_logger_otel_sdk/purple_logger_otel_sdk.dart';
import 'package:purple_otel_api/purple_otel_api.dart' show Resource, Attributes;
void main() {
// 1. Create the SDK logger provider — this is your OTel sink.
final sdkProvider = SDKLoggerProvider(
resource: Resource(Attributes.fromMap({'service.name': 'my-service'})),
processors: [
SimpleLogRecordProcessor(ConsoleLogRecordExporter()),
],
);
// 2. Wrap it in the bridge provider.
final bridgeProvider = PurpleOtelLoggerProvider(otelProvider: sdkProvider);
// 3. Build the logging pipeline with the bridge as a provider.
final factory = LoggingBuilder()
.addProvider(bridgeProvider)
.build();
// 4. Use purple_logger as usual — logs flow to OTel automatically.
final logger = factory.createLogger('order-service');
logger.info('Order placed', properties: {'orderId': 1042});
logger.warning('Slow query detected', properties: {
'query': 'SELECT * FROM orders',
'durationMs': 1250,
});
logger.error('Payment failed', error: PaymentException('timeout'));
factory.dispose();
}
Data Flow #
Every log call follows this path from purple_logger to the OTel backend:
logger.info("hello", {properties: {user: "alice"}})
│
▼
LoggerImpl.log(PurpleLogLevel.info, ...)
│ builds LogEvent with scope properties merged
│
▼
ProviderLogger.write(event)
│ checks: implements EventLogger? → yes
│
▼
PurpleOtelLogger.write(LogEvent event)
│ ① merges scopeProperties + properties
│ ② attaches error info if present
│ ③ extracts traceId/spanId from OTel context
│ ④ maps severity via severityNumber()/severityText()
│ ⑤ calls emitToOtel()
│
▼
emitToOtel(severity, body, attributes)
│ builds otel.LogRecord → logger.emit()
│
▼
SDKLogger.emit(LogRecord)
│
▼
SimpleLogRecordProcessor / BatchLogRecordProcessor
│
▼
ConsoleLogRecordExporter / OtlpHttpLogRecordExporter
│
▼
OTel Collector (OTLP) or stdout → Grafana, Azure Monitor, Jaeger, ...
Severity Mapping #
| PurpleLogLevel | OTel SeverityNumber | OTel severityText |
|---|---|---|
trace |
1 (TRACE) | TRACE |
debug |
5 (DEBUG) | DEBUG |
info |
9 (INFO) | INFO |
warning |
13 (WARN) | WARN |
error |
17 (ERROR) | ERROR |
fatal |
21 (FATAL) | FATAL |
none |
0 (UNSPECIFIED) | (empty) |
This mapping follows the OTel Logs specification and ships with the purple_logger_otel abstraction package via the OtelSeverityMapping mixin.
Trace Correlation #
When running inside an active trace span, log records automatically carry the traceId and spanId. This enables one-click navigation from a log line to its parent span in Grafana, Azure Monitor, or Jaeger.
import 'package:purple_otel_sdk/purple_otel_sdk.dart';
import 'package:purple_logger_otel_sdk/purple_logger_otel_sdk.dart';
import 'package:purple_otel_api/purple_otel_api.dart' show Resource, Attributes, Context;
void main() {
final sdkProvider = SDKLoggerProvider(
resource: Resource(Attributes.fromMap({'service.name': 'checkout'})),
processors: [SimpleLogRecordProcessor(ConsoleLogRecordExporter())],
);
final bridgeProvider = PurpleOtelLoggerProvider(otelProvider: sdkProvider);
final factory = LoggingBuilder().addProvider(bridgeProvider).build();
final tracerProvider = SDKTracerProvider(
resource: Resource(Attributes.fromMap({'service.name': 'checkout'})),
spanProcessors: [SimpleSpanProcessor(ConsoleSpanExporter())],
);
final tracer = tracerProvider.get('checkout');
// Inside this span, every log call is automatically correlated.
final span = tracer.start('process-checkout');
final ctx = Context.span(span);
ctx.run(() {
final logger = factory.createLogger('checkout');
logger.info('Payment processed'); // ← carries traceId + spanId
});
span.end();
factory.dispose();
tracerProvider.shutdown();
}
When this log record is exported, the attributes include:
{
"body": "Payment processed",
"severityText": "INFO",
"attributes": {
"traceId": "0af7651916cd43dd8448eb211c80319c",
"spanId": "b7ad6b7169203331"
}
}
The bridge extracts trace context via extractTraceContext(), which reads from otel.Context.root.span?.spanContext. If no span is active, TraceContext.empty is returned and no trace attributes are added.
Complete Setup with OTLP Export #
Ship logs to an OTel Collector or any OTLP-compatible backend:
import 'package:purple_logger/purple_logger.dart';
import 'package:purple_otel_sdk/purple_otel_sdk.dart';
import 'package:purple_logger_otel_sdk/purple_logger_otel_sdk.dart';
import 'package:purple_otel_api/purple_otel_api.dart' show Resource, Attributes;
void main() {
final otelProvider = SDKLoggerProvider(
resource: Resource(Attributes.fromMap({
'service.name': 'my-service',
'service.version': '1.0.0',
'deployment.environment': 'production',
})),
processors: [
BatchLogRecordProcessor(
OtlpHttpLogRecordExporter(
endpoint: Uri.parse('http://localhost:4318/v1/logs'),
),
),
],
);
final bridgeProvider = PurpleOtelLoggerProvider(otelProvider: otelProvider);
final factory = LoggingBuilder()
.addProvider(bridgeProvider)
.setMinimumLevel(PurpleLogLevel.info)
.build();
final logger = factory.createLogger('api-gateway');
// These logs are batched and shipped to your OTel Collector.
logger.info('Server started', properties: {'port': 8080});
logger.error('Connection refused', error: SocketException('ECONNREFUSED'));
// Graceful shutdown flushes pending records.
factory.dispose();
}
For production, prefer BatchLogRecordProcessor over SimpleLogRecordProcessor to reduce export overhead. During shutdown, factory.dispose() calls PurpleOtelLoggerProvider.dispose(), which triggers SDKLoggerProvider.shutdown() and flushes all pending log records.
Companion Packages #
| Package | Description |
|---|---|
| purple_logger | Structured logger with provider pipeline, scopes, filters, and formatters |
| purple_logger_otel | Vendor-neutral OTel bridge abstractions (zero SDK deps) |
| purple_logger_otel_sdk | This package — concrete bridge from purple_logger to PurpleOTel SDK |
| purple_otel_sdk | OpenTelemetry SDK with logging, tracing, metrics, and OTLP export |
Built by PurpleSoft #
PurpleSoft S.r.l. — software house with offices in Monza, Milano, and Lugano (Switzerland). Since 2017.
We build what doesn't exist yet.
The sectors we dominate #
Database & Storage Engines. We design, implement, and ship production-grade databases. Our LSM-tree storage engine — built in Rust for performance, exposed via FFI to Dart/Flutter, compiled to WASM for web — uses WiscKey-style key-value separation with concurrent compaction and crash recovery. It runs on all 6 platforms. This is not a wrapper around SQLite. It is a ground-up embedded NoSQL database written from scratch.
Conversational AI & Voice Assistants. We ship end-to-end AI voice platforms — from the physical device (ESP32 with custom Opus codec firmware) to the cloud backend (.NET 10, ASP.NET Core, Blazor Server) to the mobile companion app (Flutter with BLE). Our multi-agent LLM architecture orchestrates specialized agents (conversation, memory, content enrichment, research) with a multi-layered memory system spanning graph, episodic, and working memory — including adaptive forgetting, poison detection via statistical outlier analysis, and automatic episodic-to-semantic compression. Our content intelligence pipelines ingest, enrich via LLM, embed (1024-dim vectors), and serve via hybrid semantic search with HNSW indexing and training-free chunk pre-filtering — 28,000+ items enriched, 30,000+ embeddings generated.
Fintech & Payments. We build payment orchestration layers that abstract multiple gateways and cryptocurrencies behind a single API. Our engineers have shipped POS terminal firmware, fiscal receipt systems compliant with Italian regulatory standards, and cash register management platforms processing millions of transactions.
Cybersecurity & Identity. We ship post-quantum cryptography implementations using NIST-standardized algorithms on .NET 10 — the cryptography standard that will replace RSA and ECC. Our authentication infrastructure integrates SPID (Italian public digital identity) and OAuth 2.0/OpenID Connect across every major identity provider. We build digital signature platforms with PKCS#11 HSM support handling the full envelope lifecycle.
Artificial Intelligence & On-Device ML. We deploy ONNX models to phones via custom Dart runtime bindings with GPU acceleration. We integrate on-device LLM inference engines. We build neural text-to-speech engines that run across all 6 Flutter platforms and speech recognition systems with Italian dialect support. Our scientific research pipeline uses a multi-scorer verification chain with consensus voting to ensure factual accuracy in LLM outputs.
Enterprise SaaS & Cloud-Native Architecture. We architect and operate platforms at enterprise scale. Our .NET 10 monorepo spans 239 C# projects with consistent CI/CD, 297 test suites, zero build errors. We design multi-engine database abstraction layers (PostgreSQL, MySQL, Microsoft SQL Server) with automated schema-to-code generation. Our notification engine handles 6 channels with DNS-based email validation.
IoT & Embedded Systems. We write ESP32 firmware targeting ESP-IDF v5.4 with 21 FreeRTOS tasks, I²S audio pipelines, Opus codec integration, and a validated WiFi state machine. We build certificate authority infrastructure for device TLS. We write native Flutter plugins for hardware that doesn't have one yet.
Observability & DevOps. Full-stack observability is the foundation of our client solutions. We ship a complete OpenTelemetry SDK for Dart/Flutter (traces, logs, metrics, W3C propagation, OTLP export), enterprise structured logging with file rotation, and auto-instrumentation — all red-team audited with 256 automated tests.
The technologies we master #
Our engineering team works across the full stack — from ESP32 firmware to cloud-native backends to cross-platform mobile apps. We write production code in Rust (database engines, FFI), C# (.NET 10), Dart/Flutter, C (ESP-IDF, audio codecs), TypeScript, Python, and C++. Our frameworks of choice are ASP.NET Core, Blazor Server, Flutter, Angular, and React. We operate Microsoft Azure (Key Vault, Blob Storage, IoT Hub), deploy on NGINX, and manage PostgreSQL + pgvector, MySQL, Microsoft SQL Server, and ESP-IDF at scale. Our database layer uses EF Core with Npgsql, our embedded engine uses Rust + dart:ffi + WASM. Our AI stack spans LLMs, embeddings, vector search, semantic kernel, and multi-agent orchestration. We use FFmpeg for audio, LVGL for embedded displays, and BLE for device provisioning. Our CI/CD runs on Azure Pipelines.
Microsoft Partner since 2018 · SumUp Partner · Dell Partner #
Trusted by #
ABB Intesa Sanpaolo Tenaris Reply Aubay Comune di Milano BCC FIMAP Alten Altran Prometeia illimity Be Shaping the Future DS Group NVALUE Inoptim Docflow P&C
and 40+ other enterprises across banking, manufacturing, energy, and public sector.
Your project can't wait. We've solved these exact problems for companies you know. Let's solve them for you.
🌐 purplesoft.io · 📧 developers@purplesoft.io · 📞 +39 0362 148 3978 · 💼 LinkedIn · 🐙 GitHub
License #
AGPL-3.0 — see LICENSE.