How to Integrate Logging¶
Capture standard log messages from your application's logging framework as Zelos trace events.
Python¶
Python SDK includes a built-in TraceLoggingHandler
that integrates with the standard logging
module.
Quick Start¶
import logging
import zelos_sdk
from zelos_sdk.hooks.logging import TraceLoggingHandler
zelos_sdk.init()
# Add the built-in handler to capture all logs
logging.getLogger().addHandler(TraceLoggingHandler())
# Now all logs are automatically sent to Zelos
logger = logging.getLogger("my_app")
logger.info("Application started")
logger.warning("Low battery: %d%%", 15)
logger.error("Connection failed")
Configuration¶
# Configure with custom options
handler = TraceLoggingHandler(
source_name="my_application", # Default: "logger"
level=logging.INFO # Default: logging.DEBUG
)
# Add to root logger
logging.basicConfig(level=logging.INFO)
logging.getLogger().addHandler(handler)
What Gets Captured¶
Each log record becomes a trace event with these fields:
Field | Description | Example |
---|---|---|
level |
Log level name | "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" |
message |
Formatted log message | "User 123 logged in" |
name |
Logger name | "my_app.auth" |
file |
Source filename | "auth.py" |
line |
Line number | 42 |
Complete Example¶
#!/usr/bin/env python3
import datetime
import logging
import time
from zelos_sdk.hooks.logging import TraceLoggingHandler
import zelos_sdk
zelos_sdk.init()
# Setup both console and trace logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logging.getLogger().addHandler(TraceLoggingHandler())
# Create module-specific loggers
auth_logger = logging.getLogger("auth")
db_logger = logging.getLogger("database")
# All logs automatically go to Zelos
while True:
auth_logger.info("User authenticated: user_123")
db_logger.debug("Query executed in 42ms")
if datetime.datetime.now().second % 10 == 0:
auth_logger.warning("High login rate detected")
time.sleep(1)
ANSI Color Support¶
The handler preserves ANSI color codes in messages:
# Colors are preserved in the Zelos log viewer
logger.info("\033[32mSuccess!\033[0m") # Green
logger.warning("\033[33mWarning: Low disk space\033[0m") # Yellow
logger.error("\033[31mError: Connection lost\033[0m") # Red
Rust¶
Integration with log
Crate¶
use log::{Log, Metadata, Record, Level};
use zelos::TraceSource;
use zelos::trace::source::TraceSourceEvent;
use std::sync::Arc;
pub struct ZelosLogBridge {
event: Arc<TraceSourceEvent>,
}
impl ZelosLogBridge {
pub fn new(source: &TraceSource) -> Result<Self> {
let event = source
.build_event("log")
.add_string_field("level", None)
.add_string_field("name", None)
.add_string_field("message", None)
.add_string_field("file", None)
.add_u32_field("line", None)
.build()?;
Ok(Self {
event,
})
}
pub fn init(self) -> Result<()> {
log::set_boxed_logger(Box::new(self))?;
log::set_max_level(log::LevelFilter::Trace);
Ok(())
}
}
impl Log for ZelosLogBridge {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let level = match record.level() {
Level::Error => "ERROR",
Level::Warn => "WARNING",
Level::Info => "INFO",
Level::Debug => "DEBUG",
Level::Trace => "TRACE",
};
let _ = self.event.build()
.try_insert_string("level", level.to_string())
.and_then(|b| b.try_insert_string("name", record.target().to_string()))
.and_then(|b| b.try_insert_string("message", record.args().to_string()))
.and_then(|b| b.try_insert_string("file", record.file().unwrap_or("").to_string()))
.and_then(|b| b.try_insert_u32("line", record.line().unwrap_or(0)))
.and_then(|b| b.emit());
}
fn flush(&self) {}
}
// Usage
use log::{info, warn, error};
let log_source = TraceSource::new("logger", router.sender());
let bridge = ZelosLogBridge::new(&log_source)?;
bridge.init()?;
// Now use standard log macros
info!("Server started on port 8080");
warn!("Cache miss rate high: {:.2}%", 85.5);
error!("Failed to connect: {}", error_msg);
Integration with tracing
Crate¶
use tracing::{Event, Level, Subscriber};
use tracing_subscriber::{layer::Context, Layer};
use zelos::TraceSource;
use zelos::trace::source::TraceSourceEvent;
use std::sync::Arc;
pub struct ZelosTracingLayer {
event: Arc<TraceSourceEvent>,
}
impl ZelosTracingLayer {
pub fn new(source: &TraceSource) -> Result<Self> {
let event = source
.build_event("log")
.add_string_field("level", None)
.add_string_field("name", None)
.add_string_field("message", None)
.build()?;
Ok(Self {
event,
})
}
}
impl<S> Layer<S> for ZelosTracingLayer
where
S: Subscriber,
{
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
let level = match *event.metadata().level() {
Level::ERROR => "ERROR",
Level::WARN => "WARNING",
Level::INFO => "INFO",
Level::DEBUG => "DEBUG",
Level::TRACE => "TRACE",
};
let message = format!("{:?}", event);
let name = event.metadata().target();
let _ = self.event.build()
.try_insert_string("level", level.to_string())
.and_then(|b| b.try_insert_string("name", name.to_string()))
.and_then(|b| b.try_insert_string("message", message))
.and_then(|b| b.emit());
}
}
// Usage
use tracing_subscriber::prelude::*;
use tracing::{info, warn, error};
let log_source = TraceSource::new("logger", router.sender());
let zelos_layer = ZelosTracingLayer::new(&log_source)?;
tracing_subscriber::registry()
.with(zelos_layer)
.init();
// Use tracing macros
info!("Application started");
warn!(disk_usage = 0.95, "High disk usage");
error!(code = 500, "Request failed");
Go¶
Integration with slog
(Go 1.21+)¶
package main
import (
"context"
"log/slog"
"strings"
zelos "github.com/zeloscloud/zelos/go"
)
// ZelosSlogHandler implements slog.Handler
type ZelosSlogHandler struct {
source *zelos.TraceSource
event *zelos.TraceEvent
attrs []slog.Attr
group string
}
func NewZelosSlogHandler(source *zelos.TraceSource) (*ZelosSlogHandler, error) {
event, err := source.BuildEvent("log").
AddStringField("level", nil).
AddStringField("name", nil).
AddStringField("message", nil).
Build()
if err != nil {
return nil, err
}
return &ZelosSlogHandler{
source: source,
event: event,
}, nil
}
func (h *ZelosSlogHandler) Enabled(_ context.Context, _ slog.Level) bool {
return true
}
func (h *ZelosSlogHandler) Handle(_ context.Context, r slog.Record) error {
level := strings.ToUpper(r.Level.String())
// Build the message with attributes
var message strings.Builder
message.WriteString(r.Message)
// Add record attributes
r.Attrs(func(a slog.Attr) bool {
message.WriteString(" ")
message.WriteString(a.Key)
message.WriteString("=")
message.WriteString(a.Value.String())
return true
})
// Determine source name
name := "app"
if h.group != "" {
name = h.group
}
builder, _ := h.event.Build()
builder, _ = builder.TryInsertString("level", level)
builder, _ = builder.TryInsertString("name", name)
builder, _ = builder.TryInsertString("message", message.String())
return builder.Emit()
}
func (h *ZelosSlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &ZelosSlogHandler{
source: h.source,
event: h.event,
attrs: append(h.attrs, attrs...),
group: h.group,
}
}
func (h *ZelosSlogHandler) WithGroup(name string) slog.Handler {
return &ZelosSlogHandler{
source: h.source,
event: h.event,
attrs: h.attrs,
group: name,
}
}
// Usage
logSource, _ := zelos.NewTraceSource("logger", sender)
handler, _ := NewZelosSlogHandler(logSource)
// Set as default logger
logger := slog.New(handler)
slog.SetDefault(logger)
// Use slog normally
slog.Info("Server starting", "port", 8080, "env", "production")
slog.Warn("Cache miss rate high", "rate", 0.85)
slog.Error("Database connection failed", "error", err)
// With groups
dbLogger := slog.With("component", "database")
dbLogger.Info("Query executed", "duration_ms", 42)
Integration with Standard log
Package¶
package main
import (
"fmt"
"io"
"log"
"runtime"
"strings"
zelos "github.com/zeloscloud/zelos/go"
)
// ZelosLogWriter implements io.Writer to capture standard log output
type ZelosLogWriter struct {
source *zelos.TraceSource
event *zelos.TraceEvent
level string
}
func NewZelosLogWriter(source *zelos.TraceSource, level string) (*ZelosLogWriter, error) {
event, err := source.BuildEvent("log").
AddStringField("level", nil).
AddStringField("name", nil).
AddStringField("message", nil).
AddStringField("file", nil).
AddInt32Field("line", nil).
Build()
if err != nil {
return nil, err
}
return &ZelosLogWriter{
source: source,
event: event,
level: strings.ToUpper(level),
}, nil
}
func (w *ZelosLogWriter) Write(p []byte) (n int, err error) {
message := strings.TrimSpace(string(p))
// Get caller information
_, file, line, _ := runtime.Caller(2)
// Parse level from message if present
level := w.level
if strings.HasPrefix(message, "ERROR:") {
level = "ERROR"
message = strings.TrimPrefix(message, "ERROR:")
} else if strings.HasPrefix(message, "WARN:") {
level = "WARNING"
message = strings.TrimPrefix(message, "WARN:")
}
builder, _ := w.event.Build()
builder, _ = builder.TryInsertString("level", level)
builder, _ = builder.TryInsertString("name", "stdlib")
builder, _ = builder.TryInsertString("message", strings.TrimSpace(message))
builder, _ = builder.TryInsertString("file", file)
builder, _ = builder.TryInsertInt32("line", int32(line))
if err := builder.Emit(); err != nil {
return 0, err
}
return len(p), nil
}
// Usage
logSource, _ := zelos.NewTraceSource("logger", sender)
// Create writers for different levels
infoWriter, _ := NewZelosLogWriter(logSource, "INFO")
errorWriter, _ := NewZelosLogWriter(logSource, "ERROR")
// Create loggers
infoLog := log.New(infoWriter, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
errorLog := log.New(errorWriter, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
// Use standard log
infoLog.Println("Application started")
errorLog.Printf("Failed to connect: %v", err)
// Or set as default
log.SetOutput(io.MultiWriter(os.Stdout, infoWriter))
log.Println("This goes to both stdout and Zelos")
Log Viewer in Zelos App¶
The log panel in the Zelos App expects these fields:
Field | Required | Description |
---|---|---|
timestamp_ns |
Auto | Added automatically by SDK |
level |
Yes | Uppercase string (INFO, WARNING, ERROR, etc.) |
name or source |
Yes | Logger/component name |
message |
Yes | The log message text |
file |
No | Source filename (optional) |
line |
No | Line number (optional) |
Recognized Log Levels¶
The viewer applies colors based on these standard levels:
Level | Color | Common Aliases |
---|---|---|
TRACE | Gray | VERBOSE, SILLY, V, 10 |
DEBUG | Dark Gray | DBG, FINE, D, 7, 20 |
INFO | Blue | INF, NOTICE, I, 5-6, 30 |
WARNING | Yellow | WARN, WRN, W, 4, 40 |
ERROR | Red | ERR, SEVERE, E, 3, 50 |
CRITICAL | Dark Red | FATAL, PANIC, CRIT, F, 0-2, 60 |