Skip to content

Network Sink Configuration

Logly supports sending logs over various network protocols. This guide covers HTTP, TCP, UDP, and Syslog sink configuration with complete examples.

HTTP JSON Sink

POST log messages as JSON to an HTTP endpoint.

Basic HTTP Sink

python
import json
import urllib.request
from logly import logger

def http_sink(message: str) -> None:
    payload = json.dumps({"log": message}).encode("utf-8")
    request = urllib.request.Request(
        "https://logs.example.com/ingest",
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    urllib.request.urlopen(request, timeout=5)

logger.add(http_sink, level="INFO", enqueue=True)

HttpJsonSink (Built-in)

python
from logly import logger
from logly.integrations.telemetry import HttpJsonSink

logger.add(
    HttpJsonSink(
        endpoint="https://logs.example.com/ingest",
        headers={"Authorization": "Bearer YOUR_TOKEN"},
    ),
    level="WARNING",
    serialize=True,
)

logger.warning("Error sent via HTTP")

Custom HTTP Configuration

python
import json
import urllib.request
from logly import logger

def http_sink(message: str) -> None:
    payload = json.dumps({
        "log": message,
        "source": "myapp",
        "timestamp": "2026-07-01T14:30:00Z",
    }).encode("utf-8")

    request = urllib.request.Request(
        "https://logs.example.com/ingest",
        data=payload,
        headers={
            "Content-Type": "application/json",
            "Authorization": "Bearer YOUR_TOKEN",
            "X-Source": "myapp",
            "X-Environment": "production",
        },
        method="POST",
    )
    urllib.request.urlopen(request, timeout=10)

logger.add(
    http_sink,
    level="WARNING",
    enqueue=True,
)

HTTP with Retry Logic

python
import json
import urllib.request
import time
from logly import logger

def http_sink_with_retry(message: str, max_retries=3) -> None:
    payload = json.dumps({"log": message}).encode("utf-8")

    for attempt in range(max_retries):
        try:
            request = urllib.request.Request(
                "https://logs.example.com/ingest",
                data=payload,
                headers={"Content-Type": "application/json"},
                method="POST",
            )
            urllib.request.urlopen(request, timeout=5)
            return
        except Exception as e:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)
            else:
                logger.error("HTTP sink failed after {} retries: {}", max_retries, e)

logger.add(http_sink_with_retry, level="WARNING", enqueue=True)

HTTP Parameters

ParameterTypeDescription
endpointstrURL to POST logs to
headersdictHTTP headers for the request
methodstrHTTP method (default: POST)
timeoutintRequest timeout in seconds
batch_sizeintNumber of logs to batch before sending
batch_intervalfloatTime between batch sends (seconds)

TCP Sink

Send logs over a TCP socket for reliable delivery.

Basic TCP Sink

python
import socket
from logly import logger

def tcp_sink(message: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect(("logserver.example.com", 9000))
        sock.sendall(message.encode("utf-8"))

logger.add(tcp_sink, level="INFO")

TCP with Delimiter

python
import socket
from logly import logger

def tcp_sink_with_delimiter(message: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect(("logserver.example.com", 9000))
        # Add newline delimiter for line-based protocols
        sock.sendall(message.encode("utf-8") + b"\n")

logger.add(tcp_sink_with_delimiter, level="INFO")

Persistent TCP Connection

python
import socket
from logly import logger

class PersistentTCPSink:
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port
        self.sock = None

    def connect(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((self.host, self.port))

    def __call__(self, message: str) -> None:
        try:
            if self.sock is None:
                self.connect()
            self.sock.sendall(message.encode("utf-8") + b"\n")
        except (ConnectionError, OSError):
            self.sock = None
            self.connect()
            self.sock.sendall(message.encode("utf-8") + b"\n")

logger.add(PersistentTCPSink("logserver.example.com", 9000), level="INFO")

TCP Parameters

ParameterTypeDescription
hoststrServer hostname or IP
portintServer port
delimiterstrMessage delimiter (default: \n)
timeoutintConnection timeout in seconds

UDP Sink

Send logs over a UDP socket (fire-and-forget, low latency).

Basic UDP Sink

python
import socket
from logly import logger

def udp_sink(message: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
        sock.sendto(
            message.encode("utf-8"),
            ("logserver.example.com", 514),
        )

logger.add(udp_sink, level="WARNING")

UDP with Structured Data

python
import socket
import json
from logly import logger

def udp_structured_sink(message: str) -> None:
    payload = json.dumps({
        "log": message,
        "source": "myapp",
    }).encode("utf-8")
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
        sock.sendto(payload, ("logserver.example.com", 514))

logger.add(udp_structured_sink, level="WARNING")

UDP Parameters

ParameterTypeDescription
hoststrServer hostname or IP
portintServer port
max_sizeintMaximum packet size (default: 65535)

WARNING

UDP is unreliable by design. Messages may be lost during network congestion. Use UDP only for non-critical logs or metrics.

Syslog Sink

Send logs to a Syslog server using the standard logging.handlers.SysLogHandler.

Local Syslog

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

logger.add(SysLogHandler(address="/dev/log"), level="INFO")

Remote Syslog (UDP)

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

logger.add(
    SysLogHandler(address=("logserver.example.com", 514)),
    level="WARNING",
)

Remote Syslog (TCP)

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

logger.add(
    SysLogHandler(address=("logserver.example.com", 514), socktype=socket.SOCK_STREAM),
    level="WARNING",
)

Windows Syslog

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

# Requires syslog-ng or similar on Windows
logger.add(
    SysLogHandler(address=("localhost", 514)),
    level="INFO",
)

Syslog Facilities

Syslog facilities categorize the source of log messages:

FacilityDescriptionTypical Use
LOG_USERGeneric user-level messagesGeneral application logs
LOG_LOCAL0Local use 0Application-specific
LOG_LOCAL1Local use 1Application-specific
LOG_LOCAL2Local use 2Application-specific
LOG_LOCAL3Local use 3Application-specific
LOG_LOCAL4Local use 4Application-specific
LOG_LOCAL5Local use 5Application-specific
LOG_LOCAL6Local use 6Application-specific
LOG_LOCAL7Local use 7Application-specific
LOG_DAEMONSystem daemonsDaemon processes
LOG_AUTHAuthentication/securityLogin attempts
LOG_CRONCron/scheduled tasksJob scheduler
LOG_KERNKernel messagesSystem-level
LOG_MAILMail systemEmail server
LOG_NEWSNetwork newsUsenet
LOG_SYSLOGSyslog internalInternal messages

Syslog with Custom Facility

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

# Use LOG_LOCAL0 for application logs
handler = SysLogHandler(
    address=("logserver.example.com", 514),
    facility=SysLogHandler.LOG_LOCAL0,
)

logger.add(handler, level="INFO")

Syslog Severity Mapping

Logly maps its log levels to Syslog severities:

Logly LevelSyslog Severity
TRACEDEBUG
DEBUGDEBUG
INFOINFO
NOTICENOTICE
SUCCESSINFO
WARNINGWARNING
ERRORERROR
FAILCRITICAL
CRITICALCRITICAL
FATALEMERGENCY

Syslog Formats

RFC 3164 (BSD Syslog)

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

# RFC 3164 format (default)
logger.add(
    SysLogHandler(
        address=("logserver.example.com", 514),
        facility=SysLogHandler.LOG_LOCAL0,
    ),
    level="INFO",
)

Example output:

<14>Jul  1 14:30:45 myapp[12345]: INFO | Application started

RFC 5424 (Modern Syslog)

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

# For RFC 5424, use structured data
logger.add(
    SysLogHandler(
        address=("logserver.example.com", 514),
        facility=SysLogHandler.LOG_LOCAL0,
    ),
    serialize=True,
    level="INFO",
)

Example output:

<14>1 2026-07-01T14:30:45.123Z myapp 12345 - - [app@48148 service="myapp"] Application started

Network Reliability Considerations

Reliability by Protocol

ProtocolDeliveryOrderingReliabilityUse Case
HTTPGuaranteedGuaranteedHighCloud logging, structured data
TCPGuaranteedGuaranteedHighLocal network, reliable delivery
UDPBest effortMay reorderLowMetrics, non-critical logs
SyslogDepends on transportDependsMediumSystem logs, centralized logging

Error Handling

python
import json
import urllib.request
from logly import logger

def reliable_http_sink(message: str) -> None:
    try:
        payload = json.dumps({"log": message}).encode("utf-8")
        request = urllib.request.Request(
            "https://logs.example.com/ingest",
            data=payload,
            headers={"Content-Type": "application/json"},
            method="POST",
        )
        urllib.request.urlopen(request, timeout=5)
    except Exception as e:
        # Log to stderr if network sink fails
        logger.opt(exception=True).error("HTTP sink failed")

logger.add(reliable_http_sink, level="INFO", enqueue=True)

Fallback Sinks

python
from logly import logger

def primary_sink(message: str) -> None:
    # Try primary destination
    import urllib.request
    urllib.request.urlopen("https://primary.example.com/logs", data=message.encode())

def fallback_sink(message: str) -> None:
    # Fallback to local file
    with open("fallback.log", "a") as f:
        f.write(message + "\n")

# Primary: network sink
logger.add(primary_sink, level="INFO", enqueue=True, catch=True)

# Fallback: file sink
logger.add("fallback.log", level="INFO", enqueue=True)

Reconnection Behavior

Automatic Reconnection

Network sinks should handle reconnection automatically:

python
import socket
from logly import logger

class ReconnectingTCPSink:
    def __init__(self, host: str, port: int, max_retries=3):
        self.host = host
        self.port = port
        self.max_retries = max_retries
        self.sock = None

    def connect(self):
        for attempt in range(self.max_retries):
            try:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.sock.connect((self.host, self.port))
                return
            except (ConnectionError, OSError) as e:
                if attempt < self.max_retries - 1:
                    import time
                    time.sleep(2 ** attempt)
                else:
                    raise

    def __call__(self, message: str) -> None:
        try:
            if self.sock is None:
                self.connect()
            self.sock.sendall(message.encode("utf-8") + b"\n")
        except (ConnectionError, OSError):
            self.sock = None
            self.connect()
            self.sock.sendall(message.encode("utf-8") + b"\n")

logger.add(
    ReconnectingTCPSink("logserver.example.com", 9000),
    level="INFO",
    enqueue=True,
)

Queue with Retry

python
import json
import urllib.request
import time
from logly import logger

def http_sink_with_retry(message: str, max_retries=3) -> None:
    payload = json.dumps({"log": message}).encode("utf-8")

    for attempt in range(max_retries):
        try:
            request = urllib.request.Request(
                "https://logs.example.com/ingest",
                data=payload,
                headers={"Content-Type": "application/json"},
                method="POST",
            )
            urllib.request.urlopen(request, timeout=5)
            return
        except Exception as e:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)
            else:
                logger.error("HTTP sink failed after {} retries", max_retries)

logger.add(http_sink_with_retry, level="WARNING", enqueue=True)

Complete Examples

Production HTTP Logging

python
import json
import urllib.request
from logly import logger

logger.remove()

def http_json_sink(message: str) -> None:
    payload = json.dumps({
        "log": message,
        "source": "myapp",
        "environment": "production",
    }).encode("utf-8")

    request = urllib.request.Request(
        "https://logs.example.com/ingest",
        data=payload,
        headers={
            "Content-Type": "application/json",
            "Authorization": "Bearer YOUR_TOKEN",
            "X-Source": "myapp",
        },
        method="POST",
    )
    urllib.request.urlopen(request, timeout=5)

# HTTP sink for warnings and above
logger.add(
    http_json_sink,
    level="WARNING",
    enqueue=True,
)

# Local file for all logs
logger.add(
    "app.log",
    level="DEBUG",
    rotation="daily",
    retention="30 days",
    compression="gzip",
    enqueue=True,
)

# Console for immediate feedback
logger.add(
    "stderr",
    level="INFO",
    format="<green>{time:HH:mm:ss}</green> | <level>{level:<8}</level> | {message}",
    colorize=True,
)

logger.info("Application started")
logger.warning("Disk usage high")
logger.error("Connection failed")
logger.complete()

Multi-Protocol Logging

python
import socket
import json
import urllib.request
from logly import logger

logger.remove()

# TCP for reliable local delivery
def tcp_sink(message: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect(("logserver.local", 9000))
        sock.sendall(message.encode("utf-8") + b"\n")

# UDP for metrics (fire-and-forget)
def udp_sink(message: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
        sock.sendto(message.encode("utf-8"), ("logserver.local", 514))

# HTTP for cloud logging
def http_sink(message: str) -> None:
    payload = json.dumps({"log": message}).encode("utf-8")
    request = urllib.request.Request(
        "https://logs.example.com/ingest",
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    urllib.request.urlopen(request, timeout=5)

# All logs to TCP
logger.add(tcp_sink, level="DEBUG", enqueue=True)

# Warnings to HTTP
logger.add(http_sink, level="WARNING", enqueue=True)

# Metrics to UDP
logger.add(udp_sink, level="SUCCESS", enqueue=True)

# Console
logger.add("stderr", level="INFO", colorize=True)

logger.info("Debug to TCP and console")
logger.warning("Warning to all destinations")
logger.error("Error to all destinations")
logger.complete()

Syslog with Custom Facility

python
import logging
from logging.handlers import SysLogHandler
from logly import logger

logger.remove()

# Application logs to LOG_LOCAL0
app_handler = SysLogHandler(
    address=("syslog.example.com", 514),
    facility=SysLogHandler.LOG_LOCAL0,
)

# Security logs to LOG_AUTH
security_handler = SysLogHandler(
    address=("syslog.example.com", 514),
    facility=SysLogHandler.LOG_AUTH,
)

logger.add(app_handler, level="INFO")
logger.add(security_handler, level="WARNING")

# Custom level for security events
logger.level("SECURITY", no=45, color="bold red")

logger.info("Application started")
logger.warning("Configuration issue")
logger.log("SECURITY", "Unauthorized access attempt")
logger.complete()

High-Throughput Network Logging

python
import json
import urllib.request
from logly import logger
import time

logger.remove()

def batch_http_sink(message: str) -> None:
    payload = json.dumps({"log": message, "ts": time.time()}).encode("utf-8")
    request = urllib.request.Request(
        "https://logs.example.com/batch",
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    urllib.request.urlopen(request, timeout=10)

# High-throughput with batch processing
logger.add(
    batch_http_sink,
    level="INFO",
    enqueue=True,
)

# Process millions of events
for i in range(1000000):
    logger.info("Event {}", i)
    if i % 10000 == 0:
        time.sleep(0.01)  # Simulate processing time

logger.complete()

Released under the MIT License.