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

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:

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

Fluent Bit is already configured in LogWard's docker-compose.yml:

yaml
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

Next Steps

Want to explore SDKs for advanced features? Check out the SDK documentation for Node.js, Python, PHP, and Kotlin.