No-SDK Setup
Use LogWard without installing any SDK. Just print to stdout - works with any language (Go, Rust, Python, Java, .NET, Ruby, PHP).
No SDK Required
LogWard supports stdout-based logging via Fluent Bit (self-hosted) or Docker logging drivers (cloud). Simply write to stdout/stderr and your logs will be automatically ingested.
Choose Your Method
Use Docker's Fluentd logging driver to send logs directly to LogWard Cloud.
- No Fluent Bit container needed
- Configure once in docker-compose.yml
- Zero code changes required
Add Fluent Bit to your LogWard installation to collect logs from all Docker containers automatically.
- Optional Fluent Bit configuration
- Automatic metadata enrichment
- Full control over log processing
How It Works
LogWard uses Fluent Bit to automatically collect logs from Docker containers:
/var/lib/docker/containers/*/./api/v1/ingest/single with
your API key automatically.Setup Instructions (Cloud)
For LogWard Cloud, configure your Docker containers to use the Fluentd logging driver:
1. Configure docker-compose.yml
Add logging configuration to each service in your docker-compose.yml:
services:
my-app:
image: my-app:latest
logging:
driver: fluentd
options:
fluentd-address: api.logward.dev:24224
fluentd-async: "true"
tag: "lp_your_api_key_here.{{.Name}}"
labels: "service"
labels:
service: "my-app"Coming Soon
LogWard Cloud is currently in beta. Sign up at logward.dev to get early access and we'll notify you when Fluentd logging driver support is available.
Setup Instructions (Self-Hosted)
1. Generate API Key
Create an API key in your LogWard project settings.
2. Configure Fluent Bit API Key
Add your API key to the docker/.env file:
# In docker/.env
FLUENT_BIT_API_KEY=lp_your_api_key_here3. Restart Fluent Bit
docker compose restart fluent-bitSetup Complete
All Docker containers now automatically send logs to LogWard. No code changes needed!
Language Examples
Just write to stdout/stderr using your language's standard logging. No special library needed.
Go
package main
import (
"log"
"encoding/json"
)
func main() {
// Simple text logging
log.Println("Server started on port 8080")
// Structured logging (JSON format recommended)
logData := map[string]interface{}{
"level": "info",
"message": "User logged in",
"user_id": 12345,
"ip": "192.168.1.1",
}
json.NewEncoder(os.Stdout).Encode(logData)
}Rust
use log::{info, error};
use env_logger;
fn main() {
env_logger::init();
info!("Application started");
error!("Database connection failed");
// Or use serde_json for structured logs
println!("{}", serde_json::json!({
"level": "info",
"message": "Request processed",
"duration_ms": 150
}));
}Python
import logging
import json
# Standard logging
logging.basicConfig(level=logging.INFO)
logging.info("Application started")
# Structured JSON logging
log_data = {
"level": "info",
"message": "User action",
"user_id": 12345,
"action": "login"
}
print(json.dumps(log_data))Java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
public class App {
private static final Logger logger = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
// Simple logging
logger.info("Application started");
// Structured logging (use Logback with JSON encoder)
Map<String, Object> logData = new HashMap<>();
logData.put("level", "info");
logData.put("message", "Request received");
logData.put("request_id", "abc123");
System.out.println(new ObjectMapper().writeValueAsString(logData));
}
}.NET (C#)
using Serilog;
using System.Text.Json;
class Program
{
static void Main()
{
// Using Serilog with console sink
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
Log.Information("Application started");
Log.Error("Database connection failed", new { UserId = 123 });
// Or manual JSON
var logData = new {
level = "info",
message = "User login",
user_id = 12345
};
Console.WriteLine(JsonSerializer.Serialize(logData));
}
}Ruby
require 'logger'
require 'json'
# Standard logging
logger = Logger.new(STDOUT)
logger.info("Application started")
# Structured JSON logging
log_data = {
level: 'info',
message: 'User action',
user_id: 12345,
action: 'login'
}
puts log_data.to_jsonPHP
<?php
// Simple logging
error_log("Application started");
// Structured JSON logging
$logData = [
'level' => 'info',
'message' => 'User login',
'user_id' => 12345,
'ip' => $_SERVER['REMOTE_ADDR']
];
echo json_encode($logData) . "\n";
?>Docker Configuration
Add Fluent Bit to your docker-compose.yml to enable
automatic log collection from Docker containers:
# Add this service to your docker-compose.yml
fluent-bit:
image: fluent/fluent-bit:latest
container_name: logward-fluent-bit
volumes:
- ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
- ./parsers.conf:/fluent-bit/etc/parsers.conf:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
LOGWARD_API_KEY: ${FLUENT_BIT_API_KEY}
LOGWARD_API_HOST: backend
depends_on:
- backend
restart: unless-stopped- Tails logs from
/var/lib/docker/containers/*/*.log - Parses Docker JSON logs and extracts metadata
- Adds container name, ID, and image as metadata
- Sends to LogWard API with automatic retries (3x)
- Flushes logs every 5 seconds
Fluent Bit Configuration (Advanced)
If you want to customize Fluent Bit or use it outside of LogWard's Docker Compose setup, here's the complete configuration:
fluent-bit.conf
Main configuration file for Fluent Bit. This collects Docker container logs and sends them to LogWard:
# Fluent Bit Configuration for LogWard
[SERVICE]
# Flush logs every 5 seconds
Flush 5
# Run in foreground
Daemon Off
# Log level (error, warning, info, debug, trace)
Log_Level info
# Parsers configuration file
Parsers_File /fluent-bit/etc/parsers.conf
# =============================================================================
# INPUT - Docker Container Logs
# =============================================================================
[INPUT]
Name tail
Path /var/lib/docker/containers/*/*.log
Parser docker
Tag docker.*
Refresh_Interval 5
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Path_Key filepath
# =============================================================================
# FILTER - Parse and Enrich
# =============================================================================
# Extract container metadata (name, id, image)
[FILTER]
Name parser
Match docker.*
Key_Name log
Parser docker_json
Reserve_Data On
Preserve_Key On
# Add required fields for LogWard API
[FILTER]
Name modify
Match docker.*
# Set default level if not present
Add level info
# Rename 'log' field to 'message'
Rename log message
# Set service name from container_name
Copy container_name service
# Remove unnecessary fields to reduce log size
[FILTER]
Name record_modifier
Match docker.*
Remove_key stream
Remove_key filepath
Remove_key container_name
# =============================================================================
# OUTPUT - Send to LogWard
# =============================================================================
[OUTPUT]
Name http
Match docker.*
Host ${LOGWARD_API_HOST}
Port 8080
URI /api/v1/ingest/single
Format json_lines
Header X-API-Key ${LOGWARD_API_KEY}
Header Content-Type application/json
# Date/time settings
Json_date_key time
Json_date_format iso8601
# Retry settings
Retry_Limit 3
# TLS (disable for internal Docker network)
tls Offparsers.conf
Parser definitions for Docker JSON logs:
# Fluent Bit Parsers Configuration
# Parser for Docker JSON logs
[PARSER]
Name docker_json
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
# Parser for Docker container logs
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep Onextract_container_id.lua (Optional)
Lua script to extract container ID from file path (used in LogWard's setup for metadata enrichment):
-- Extract container ID from Docker log file path
-- Path format: /var/lib/docker/containers/<container_id>/<container_id>-json.log
function extract_container_id(tag, timestamp, record)
local filepath = record["filepath"]
if filepath == nil then
return 0, timestamp, record
end
-- Extract container ID from path
-- Example: /var/lib/docker/containers/abc123.../abc123...-json.log
local container_id = filepath:match("/var/lib/docker/containers/([^/]+)/")
if container_id then
record["container_id"] = container_id
record["container_short_id"] = container_id:sub(1, 12)
end
-- Return code: 1 = modified and keep, 0 = no change
return 1, timestamp, record
endUsage with docker-compose.yml
If you want to add Fluent Bit to your own project (not using LogWard's Docker Compose):
services:
fluent-bit:
image: fluent/fluent-bit:latest
container_name: my-fluent-bit
volumes:
# Mount configuration files
- ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
- ./parsers.conf:/fluent-bit/etc/parsers.conf:ro
# Optional: Lua scripts for advanced processing
- ./extract_container_id.lua:/fluent-bit/etc/extract_container_id.lua:ro
# Docker logs directory (read-only)
- /var/lib/docker/containers:/var/lib/docker/containers:ro
# Docker socket (read-only)
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
# For LogWard Cloud
LOGWARD_API_KEY: lp_your_api_key_here
LOGWARD_API_HOST: api.logward.dev
# Or for self-hosted
# LOGWARD_API_KEY: lp_your_api_key_here
# LOGWARD_API_HOST: localhost # or your server IP
restart: unless-stopped
networks:
- your-network
networks:
your-network:
driver: bridgeImportant Notes
- • Fluent Bit needs access to
/var/lib/docker/containersto read container logs - • The
LOGWARD_API_KEYenvironment variable is used in the configuration via${LOGWARD_API_KEY} - • For cloud use, set
LOGWARD_API_HOST=api.logward.devand use HTTPS in the OUTPUT section - • Logs are batched and flushed every 5 seconds for efficiency
- • Failed sends are retried up to 3 times with exponential backoff
SDK vs No-SDK: When to Use Each
- You want zero code changes (just deploy)
- You're using multiple languages (Go, Rust, Ruby, etc.)
- You prefer centralized log collection (Fluent Bit)
- You're running in Docker/Kubernetes (stdout is standard)
- You need advanced features (circuit breaker, retries)
- You want direct API control (batching, buffering)
- You're not using Docker (e.g., serverless, VMs)
- You prefer programmatic logging (type-safe APIs)
Best Practices
Instead of plain text, log in JSON format for better parsing:
{
"level": "error",
"message": "Payment failed",
"user_id": 12345,
"amount": 99.99,
"error_code": "INSUFFICIENT_FUNDS"
}Add trace IDs to correlate logs across services (for distributed tracing).
Standardize levels: debug, info, warn, error, critical. Fluent Bit defaults to info if not specified.
Fluent Bit automatically adds container_name as service. Use descriptive container names in docker-compose.yml.
Troubleshooting
Logs not appearing in LogWard?
- Check Fluent Bit is running:
docker compose ps fluent-bit - Check Fluent Bit logs:
docker compose logs fluent-bit - Verify API key in
docker/.env:FLUENT_BIT_API_KEY=lp_... - Ensure your app is logging to stdout (not files):
docker compose logs your-service - Check LogWard backend logs:
docker compose logs backend | grep ingest
Logs have wrong service name?
Fluent Bit uses container_name from docker-compose.yml. Set descriptive names:
services:
my-app:
container_name: my-app-production # This becomes the service name
image: my-app:latestCollecting Syslog from Infrastructure?
Want to collect logs from Proxmox, ESXi, firewalls, or network devices? Check out the Syslog Integration guide.
Next Steps
Want to explore SDKs for advanced features? Check out the SDK documentation for Node.js, Python, Go, PHP, and Kotlin.