Claude Code for syslog: Python Unix Syslog Interface — Claude Skills 360 Blog
Blog / AI / Claude Code for syslog: Python Unix Syslog Interface
AI

Claude Code for syslog: Python Unix Syslog Interface

Published: October 26, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s syslog module writes to the Unix syslog daemon (Linux/macOS only). import syslog. openlog: syslog.openlog(ident, logoption, facility) — sets program name prefix; call once at startup. closelog: syslog.closelog() — closes syslog connection (auto-reopens on next call). syslog: syslog.syslog(priority, message) or syslog.syslog(message) (defaults to LOG_INFO). setlogmask: syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_WARNING)) — suppress DEBUG/INFO. Priority = facility | severity: syslog.LOG_USER | syslog.LOG_ERR. Severity constants (high→low): LOG_EMERG=0, LOG_ALERT=1, LOG_CRIT=2, LOG_ERR=3, LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6, LOG_DEBUG=7. Facility constants: LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LOCAL0LOG_LOCAL7. Option flags: LOG_PID (append PID), LOG_CONS (fall back to console), LOG_NDELAY (connect immediately), LOG_PERROR (also write to stderr). Messages appear in /var/log/syslog, /var/log/messages, or via journalctl -t ident on systemd systems. Claude Code generates daemon loggers, audit trail emitters, structured syslog handlers, and priority-routed log pipelines.

CLAUDE.md for syslog

## syslog Stack
- Stdlib: import syslog  (Linux/macOS only)
- Init:   syslog.openlog("myapp", syslog.LOG_PID, syslog.LOG_DAEMON)
- Write:  syslog.syslog(syslog.LOG_ERR, "something failed")
- Filter: syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_WARNING))
- Close:  syslog.closelog()
- Priority = facility | severity, or severity alone (uses facility from openlog)

syslog Unix Syslog Pipeline

# app/syslogutil.py — openlog wrapper, Python logging handler, audit emitter
from __future__ import annotations

import logging
import os
import platform
from dataclasses import dataclass, field
from typing import ClassVar

_SYSLOG_AVAILABLE = platform.system() != "Windows"
if _SYSLOG_AVAILABLE:
    import syslog as _syslog


# ─────────────────────────────────────────────────────────────────────────────
# 1. Convenience open / write / close
# ─────────────────────────────────────────────────────────────────────────────

# Severity name → constant
_SEVERITY: dict[str, int] = {}
_FACILITY: dict[str, int] = {}

if _SYSLOG_AVAILABLE:
    _SEVERITY = {
        "EMERG":   _syslog.LOG_EMERG,
        "ALERT":   _syslog.LOG_ALERT,
        "CRIT":    _syslog.LOG_CRIT,
        "ERR":     _syslog.LOG_ERR,
        "WARNING": _syslog.LOG_WARNING,
        "NOTICE":  _syslog.LOG_NOTICE,
        "INFO":    _syslog.LOG_INFO,
        "DEBUG":   _syslog.LOG_DEBUG,
    }
    _FACILITY = {
        "USER":    _syslog.LOG_USER,
        "DAEMON":  _syslog.LOG_DAEMON,
        "AUTH":    _syslog.LOG_AUTH,
        "SYSLOG":  _syslog.LOG_SYSLOG,
        "LOCAL0":  _syslog.LOG_LOCAL0,
        "LOCAL1":  _syslog.LOG_LOCAL1,
        "LOCAL2":  _syslog.LOG_LOCAL2,
        "LOCAL3":  _syslog.LOG_LOCAL3,
        "LOCAL4":  _syslog.LOG_LOCAL4,
        "LOCAL5":  _syslog.LOG_LOCAL5,
        "LOCAL6":  _syslog.LOG_LOCAL6,
        "LOCAL7":  _syslog.LOG_LOCAL7,
    }


def open_syslog(
    ident: str,
    facility: str = "USER",
    options: int | None = None,
) -> None:
    """
    Open a connection to syslog with the given program identity and facility.

    Example:
        open_syslog("myapp", facility="DAEMON")
    """
    if not _SYSLOG_AVAILABLE:
        return
    if options is None:
        options = _syslog.LOG_PID
    fac = _FACILITY.get(facility.upper(), _syslog.LOG_USER)
    _syslog.openlog(ident, options, fac)


def close_syslog() -> None:
    """Close the syslog connection."""
    if _SYSLOG_AVAILABLE:
        _syslog.closelog()


def log(severity: str | int, message: str) -> None:
    """
    Write a message to syslog at the given severity.
    severity can be a string ('ERR', 'INFO', etc.) or an int constant.

    Example:
        log("ERR", "disk write failed: /var/data")
        log("INFO", "worker started")
    """
    if not _SYSLOG_AVAILABLE:
        print(f"[syslog/{severity}] {message}")
        return
    if isinstance(severity, str):
        priority = _SEVERITY.get(severity.upper(), _syslog.LOG_INFO)
    else:
        priority = severity
    _syslog.syslog(priority, message)


def set_min_level(min_severity: str) -> None:
    """
    Suppress syslog messages below min_severity.
    E.g. set_min_level("WARNING") hides DEBUG and INFO.

    Example:
        set_min_level("WARNING")
    """
    if not _SYSLOG_AVAILABLE:
        return
    level = _SEVERITY.get(min_severity.upper(), _syslog.LOG_DEBUG)
    _syslog.setlogmask(_syslog.LOG_UPTO(level))


# ─────────────────────────────────────────────────────────────────────────────
# 2. Python logging.Handler integration
# ─────────────────────────────────────────────────────────────────────────────

class SyslogHandler(logging.Handler):
    """
    A logging.Handler that routes Python log records to Unix syslog.
    Integrates with the standard logging hierarchy.

    Example:
        logger = logging.getLogger("myapp")
        logger.addHandler(SyslogHandler("myapp", facility="DAEMON"))
        logger.setLevel(logging.DEBUG)
        logger.error("something failed")
    """

    # Mapping from Python logging levels to syslog severities
    _LEVEL_MAP: ClassVar[dict[int, int]] = {}

    def __init__(
        self,
        ident: str = "python",
        facility: str = "USER",
        options: int | None = None,
        level: int = logging.NOTSET,
    ) -> None:
        super().__init__(level)
        self._ident = ident
        if _SYSLOG_AVAILABLE:
            self.__class__._LEVEL_MAP = {
                logging.DEBUG:    _syslog.LOG_DEBUG,
                logging.INFO:     _syslog.LOG_INFO,
                logging.WARNING:  _syslog.LOG_WARNING,
                logging.ERROR:    _syslog.LOG_ERR,
                logging.CRITICAL: _syslog.LOG_CRIT,
            }
            open_syslog(ident, facility=facility, options=options)

    def emit(self, record: logging.LogRecord) -> None:
        if not _SYSLOG_AVAILABLE:
            return
        try:
            msg = self.format(record)
            priority = self._LEVEL_MAP.get(record.levelno, _syslog.LOG_INFO)
            _syslog.syslog(priority, msg)
        except Exception:
            self.handleError(record)

    def close(self) -> None:
        if _SYSLOG_AVAILABLE:
            _syslog.closelog()
        super().close()


def make_syslog_logger(
    name: str,
    ident: str | None = None,
    facility: str = "USER",
    min_level: int = logging.INFO,
) -> logging.Logger:
    """
    Return a logger that writes to syslog (and stderr as fallback).
    The logger writes only to syslog on POSIX; stderr fallback on Windows.

    Example:
        logger = make_syslog_logger("myapp.worker", facility="DAEMON")
        logger.info("worker started pid=%d", os.getpid())
        logger.error("task failed: %s", exc)
    """
    logger = logging.getLogger(name)
    logger.setLevel(min_level)
    if not logger.handlers:
        if _SYSLOG_AVAILABLE:
            handler = SyslogHandler(ident or name, facility=facility)
        else:
            handler = logging.StreamHandler()
        handler.setFormatter(logging.Formatter("%(name)s: %(levelname)s %(message)s"))
        logger.addHandler(handler)
    return logger


# ─────────────────────────────────────────────────────────────────────────────
# 3. Audit-trail emitter
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class AuditEvent:
    """Structured audit event for compliance / security logging."""
    action:   str
    user:     str      = field(default_factory=lambda: str(os.getuid()))
    resource: str      = ""
    result:   str      = "ok"   # "ok" or "denied"
    detail:   str      = ""
    pid:      int      = field(default_factory=os.getpid)

    def to_syslog_msg(self) -> str:
        parts = [
            f"action={self.action}",
            f"user={self.user}",
            f"pid={self.pid}",
            f"result={self.result}",
        ]
        if self.resource:
            parts.append(f"resource={self.resource!r}")
        if self.detail:
            parts.append(f"detail={self.detail!r}")
        return "AUDIT " + " ".join(parts)


def emit_audit(
    action: str,
    user: str | None = None,
    resource: str = "",
    result: str = "ok",
    detail: str = "",
    facility: str = "AUTH",
    ident: str = "audit",
) -> AuditEvent:
    """
    Write a structured audit event to syslog at LOG_NOTICE.
    Uses LOG_AUTH facility by default (visible in /var/log/auth.log).

    Example:
        emit_audit("login", user="alice", result="ok")
        emit_audit("delete", user="bob", resource="/etc/passwd", result="denied")
    """
    if user is None:
        try:
            import pwd
            user = pwd.getpwuid(os.getuid()).pw_name
        except (ImportError, KeyError):
            user = str(os.getuid())

    event = AuditEvent(action=action, user=user,
                       resource=resource, result=result,
                       detail=detail)
    msg = event.to_syslog_msg()

    if _SYSLOG_AVAILABLE:
        fac = _FACILITY.get(facility.upper(), _syslog.LOG_AUTH)
        priority = fac | _syslog.LOG_NOTICE
        _syslog.openlog(ident, _syslog.LOG_PID, fac)
        _syslog.syslog(priority, msg)
    else:
        print(f"[{facility}/NOTICE] {msg}")

    return event


# ─────────────────────────────────────────────────────────────────────────────
# 4. Priority routing
# ─────────────────────────────────────────────────────────────────────────────

class PriorityRouter:
    """
    Routes messages to different syslog facilities based on severity.
    Useful for daemons that want errors in LOG_DAEMON and audits in LOG_AUTH.

    Example:
        router = PriorityRouter("myapp")
        router.log("ERR", "disk full")
        router.audit("file_delete", resource="/tmp/x")
    """

    def __init__(self, ident: str) -> None:
        self._ident = ident
        self._operational_logger = make_syslog_logger(
            f"{ident}.ops", ident=ident, facility="DAEMON"
        )

    def log(self, severity: str, message: str) -> None:
        """Route an operational message to LOG_DAEMON."""
        level_map = {
            "DEBUG": logging.DEBUG,
            "INFO": logging.INFO,
            "NOTICE": logging.INFO,
            "WARNING": logging.WARNING,
            "ERR": logging.ERROR,
            "CRIT": logging.CRITICAL,
            "ALERT": logging.CRITICAL,
            "EMERG": logging.CRITICAL,
        }
        level = level_map.get(severity.upper(), logging.INFO)
        self._operational_logger.log(level, message)

    def audit(
        self,
        action: str,
        user: str | None = None,
        resource: str = "",
        result: str = "ok",
        detail: str = "",
    ) -> AuditEvent:
        """Route an audit event to LOG_AUTH."""
        return emit_audit(
            action=action, user=user, resource=resource,
            result=result, detail=detail,
            facility="AUTH", ident=self._ident,
        )


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== syslog demo ===")

    if not _SYSLOG_AVAILABLE:
        print("  syslog not available on Windows; messages would go to stderr")

    # ── basic open / log / close ───────────────────────────────────────────────
    print("\n--- basic log ---")
    open_syslog("syslog-demo", facility="USER")
    log("INFO",    "demo started")
    log("WARNING", "example warning from syslog demo")
    log("ERR",     "example error from syslog demo")
    log("DEBUG",   "this debug message is visible by default")
    close_syslog()
    print("  logged INFO / WARNING / ERR / DEBUG to syslog")
    print("  check: sudo journalctl -t syslog-demo  OR  /var/log/syslog")

    # ── setlogmask ────────────────────────────────────────────────────────────
    print("\n--- setlogmask ---")
    open_syslog("syslog-demo", facility="USER")
    set_min_level("WARNING")
    log("DEBUG", "this DEBUG is suppressed by mask")
    log("INFO",  "this INFO is suppressed by mask")
    log("WARNING", "this WARNING should appear")
    close_syslog()
    print("  only WARNING and above passed through setlogmask")

    # ── Python logging integration ─────────────────────────────────────────────
    print("\n--- Python logging handler ---")
    logger = make_syslog_logger("syslog-demo.worker", facility="USER")
    logger.info("worker started pid=%d", os.getpid())
    logger.warning("high memory usage: %dMB", 512)
    logger.error("task failed: division by zero")
    print("  Python logging → SyslogHandler → syslogd")

    # ── audit events ──────────────────────────────────────────────────────────
    print("\n--- emit_audit ---")
    ev1 = emit_audit("login",  user="alice",  result="ok",     facility="AUTH")
    ev2 = emit_audit("delete", user="bob",    result="denied",
                     resource="/etc/shadow",  facility="AUTH")
    print(f"  {ev1.to_syslog_msg()}")
    print(f"  {ev2.to_syslog_msg()}")

    # ── PriorityRouter ────────────────────────────────────────────────────────
    print("\n--- PriorityRouter ---")
    router = PriorityRouter("syslog-demo")
    router.log("INFO", "service started cleanly")
    router.log("ERR",  "upstream connection refused")
    router.audit("config_change", resource="/etc/myapp.conf")
    print("  operational messages → LOG_DAEMON, audits → LOG_AUTH")

    print("\n=== done (messages written to syslog) ===")

For the logging alternative — Python’s logging module with logging.handlers.SysLogHandler(address='/dev/log') provides cross-platform structured log emission with formatters, filters, level hierarchy, and handler chaining — use logging.handlers.SysLogHandler when you need structured log formatting, multiple output destinations (file + syslog simultaneously), log rotation, or cross-platform code; use syslog directly when you want the simplest possible connection to the native syslog daemon with zero configuration, or when writing audit events that should use LOG_AUTH facility semantics that SysLogHandler can also target via the facility parameter. For the systemd journal alternative — on systemd systems, writing to stderr with the format <N>message (where N is the journal priority integer: <3> for ERR, <6> for INFO) or using systemd-python’s journal.send() sends structured entries with MESSAGE=, PRIORITY=, SYSLOG_IDENTIFIER=, and custom fields — use journal writing when deploying systemd services that benefit from journalctl -u service --output json queryability; use syslog when targeting legacy /var/log/syslog infrastructure or when LOG_LOCAL0LOG_LOCAL7 facility routing to remote rsyslog/syslog-ng is required. The Claude Skills 360 bundle includes syslog skill sets covering open_syslog()/log()/set_min_level()/close_syslog() primitives, SyslogHandler Python logging integration with make_syslog_logger(), AuditEvent with emit_audit() compliance trail emitter, and PriorityRouter for multi-facility routing. Start with the free tier to try syslog daemon patterns and syslog pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free