// Syslog both ways: a program's own events sent to a log server (rsyslog, syslog-ng, Graylog,
// Papertrail) over UDP, TCP or TLS, and a small syslog receiver that reads what routers and
// servers send it over UDP.
//
//   SYSLOG_TCP=tcp://127.0.0.1:514 SYSLOG_TLS=tls://logs.example.com:6514 quillo run syslog.qwe
//   then open http://localhost:8080/format, /parse, /send and /received
//
// SYSLOG_CA names a PEM file to trust for TLS (a server with its own CA); without it the
// public roots are trusted. SYSLOG_LISTEN is the UDP port this program receives on (5514).

use "syslog.qwer"

// The receiver: each datagram read, its structured data too, the last 100 kept.
let LISTEN = int(env("SYSLOG_LISTEN") ?? "5514")
let received = []
udp.listen(LISTEN, (p) => {
    let m = syslog_parse(p.text ?? "")
    let row = is_err(m) ? { from: p.from.host, error: m.error } : { from: p.from.host, severity: m.severity, app: m.app, message: m.message, data: m.structured_data }
    received = slice(received + [row], -100)
}, { host: "127.0.0.1" })

route GET /received = { received }

route GET /format = {
    rfc5424: syslog_format({
        facility: "local4", severity: "err", time: "2026-10-30T09:15:02.120Z", host: "shop-1", app: "checkout", procid: 4120, msgid: "PAY",
        structured_data: { "order@32473": { id: "A-1042", amount: "59.90" }, origin: { ip: ["10.0.0.5", "10.0.0.6"] } },
        message: "payment declined",
    })?,
    bsd: syslog_format({ format: "3164", facility: "auth", severity: "warning", time: "2026-10-03T07:04:00Z", host: "gw", app: "sshd", procid: 811, message: "Failed password for root" })?,
    bad: syslog_format({ app: "my app" }).error,
}

route GET /parse {
    // The examples of RFC 5424 and RFC 3164.
    let a = syslog_parse(`<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"][examplePriority@32473 class="high"] An application event log entry`)?
    let b = syslog_parse("<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8")?
    {
        a: { facility: a.facility, severity: a.severity, host: a.host, app: a.app, msgid: a.msgid, time: a.time, event: a.structured_data["exampleSDID@32473"].eventID, class: a.structured_data["examplePriority@32473"].class, message: a.message },
        b: { facility: b.facility, severity: b.severity, host: b.host, app: b.app, message: b.message },
        stream: syslog_parse_many("44 <13>1 2026-10-30T10:00:00Z h app - - - first\n<14>Oct 30 10:00:01 h app[7]: second\n") | map((m) => m.message),
        bad: syslog_parse("no priority").error,
    }
}

route GET /send {
    let event = { facility: "local0", severity: "notice", app: "shop", msgid: "ORDER", structured_data: { "order@32473": { id: "A-1042", total: "59.90" } }, message: "order placed" }
    let ca = env("SYSLOG_CA", null)
    let tcp = env("SYSLOG_TCP", null)
    let tls = env("SYSLOG_TLS", null)
    let udp = env("SYSLOG_UDP", null)
    let out = {
        // To this program's own receiver, and to a log server over UDP, TCP and TLS.
        own: syslog_send(`udp://127.0.0.1:${LISTEN}`, event, {})?,
        udp: udp ? syslog_send(udp, event, {})? : "set SYSLOG_UDP",
        tcp: tcp ? syslog_send(tcp, event, {})? : "set SYSLOG_TCP",
        tcp_lines: tcp ? syslog_send_many(tcp, [event + { severity: "info", message: "line one" }, "<14>1 - - shop - - - line two"], { framing: "newline" })? : null,
        tls: tls ? syslog_send(tls, event + { message: "order placed (over TLS)" }, { ca_file: ca })? : "set SYSLOG_TLS",
        refused: syslog_send("tcp://127.0.0.1:1", event, { timeout_ms: 500 }).error,
    }
    sleep(100)
    out + { received: received[-1] }
}
