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

Cloud (api.logward.dev)

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
Self-Hosted

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:

1
Your app writes to stdout/stderr
Use standard console.log, print(), fmt.Println() - whatever your language provides.
2
Docker captures container output
Docker's logging driver stores logs in JSON format at /var/lib/docker/containers/*/.
3
Fluent Bit collects and enriches
Fluent Bit tails container logs, adds metadata (container name, service), and formats for LogWard.
4
Logs sent to LogWard API
Fluent Bit sends logs to /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:

yaml
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:

bash
# In docker/.env
FLUENT_BIT_API_KEY=lp_your_api_key_here

3. Restart Fluent Bit

bash
docker compose restart fluent-bit

Setup 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

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

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

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

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#)

csharp
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

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_json

PHP

php
<?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:

yaml
# 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
What Fluent Bit Does
  • 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:

conf
# 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                 Off

parsers.conf

Parser definitions for Docker JSON logs:

conf
# 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   On

extract_container_id.lua (Optional)

Lua script to extract container ID from file path (used in LogWard's setup for metadata enrichment):

lua
-- 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
end

Usage with docker-compose.yml

If you want to add Fluent Bit to your own project (not using LogWard's Docker Compose):

yaml
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: bridge

Important Notes

  • • Fluent Bit needs access to /var/lib/docker/containers to read container logs
  • • The LOGWARD_API_KEY environment variable is used in the configuration via ${LOGWARD_API_KEY}
  • • For cloud use, set LOGWARD_API_HOST=api.logward.dev and 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

Use No-SDK When:
  • 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)
Use SDK When:
  • 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

1. Use Structured Logging (JSON)

Instead of plain text, log in JSON format for better parsing:

json
{
  "level": "error",
  "message": "Payment failed",
  "user_id": 12345,
  "amount": 99.99,
  "error_code": "INSUFFICIENT_FUNDS"
}
2. Include Trace/Request IDs

Add trace IDs to correlate logs across services (for distributed tracing).

3. Use Log Levels

Standardize levels: debug, info, warn, error, critical. Fluent Bit defaults to info if not specified.

4. Add Service Metadata

Fluent Bit automatically adds container_name as service. Use descriptive container names in docker-compose.yml.

Troubleshooting

Logs not appearing in LogWard?

  1. Check Fluent Bit is running: docker compose ps fluent-bit
  2. Check Fluent Bit logs: docker compose logs fluent-bit
  3. Verify API key in docker/.env: FLUENT_BIT_API_KEY=lp_...
  4. Ensure your app is logging to stdout (not files): docker compose logs your-service
  5. 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:

yaml
services:
  my-app:
    container_name: my-app-production  # This becomes the service name
    image: my-app:latest

Collecting 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.