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
LogWard includes Fluent Bit pre-configured to collect logs from all Docker containers.
- Fluent Bit included in docker-compose
- 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
Fluent Bit is already configured in LogWard's 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:latestNext Steps
Want to explore SDKs for advanced features? Check out the SDK documentation for Node.js, Python, PHP, and Kotlin.