Last active
March 23, 2021 13:30
-
-
Save portante/ff7fb429c6f973aab377f7bb77b0ffdb to your computer and use it in GitHub Desktop.
Simple C program to demonstrate how to properly "tail" journald, what journalctl -f does, while detecting journal file rotation to avoid keeping deleted files open (basically call sd_journal_get_fd() immediately after sd_journal_open()).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// cc -g2 example_journal_reader.c -lsystemd | |
#include <stdio.h> | |
#include <time.h> | |
#include <errno.h> | |
#include <sys/poll.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <systemd/sd-journal.h> | |
int main(int argc, char *argv[]) { | |
uint64_t prev_timestamp = 0; | |
int r; | |
sd_journal *j; | |
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); | |
if (r < 0) { | |
fprintf(stderr, "Failed to open journal: %s\n", strerror(-r)); | |
return 1; | |
} | |
// Prime the journal internals to ensure sd_journal_wait() actually waits. | |
// NOTE: calling sd_journal_get_fd() here "primes the pump" for the | |
// sd_journal_wait() to work properly in the face of journal file rotation. | |
r = sd_journal_get_fd(j); | |
if (r < 0) { | |
fprintf(stderr, "Failed to prime journal fd state: %s\n", strerror(-r)); | |
return 1; | |
} | |
// This program only emits new records from the time it opens the journal. | |
r = sd_journal_seek_tail(j); | |
if (r < 0) { | |
fprintf(stderr, "Failed to seek to tail of journal: %s\n", strerror(-r)); | |
return 1; | |
} | |
// For some reason, fetching previous then next avoids getting an initial | |
// huge chunk of records. If one calls sd_journal_seek_tail() and then | |
// calls sd_journal_next() we receive the huge chunk. | |
r = sd_journal_previous(j); | |
if (r < 0) { | |
fprintf(stderr, "Failed to get previous journal entry: %s\n", strerror(-r)); | |
return 1; | |
} | |
r = sd_journal_next(j); | |
while (r >= 0) { | |
if (r == 0) { | |
// We wait in a loop to catch spurious NOPs | |
do { | |
jr = sd_journal_wait(j, (uint64_t)-1); | |
if (jr < 0) { | |
fprintf(stderr, "Failed to wait for journal changes: %s\n", strerror(-jr)); | |
return 1; | |
} | |
} while (jr == SD_JOURNAL_NOP); | |
r = sd_journal_next(j); | |
continue; | |
} | |
const char *d; | |
size_t l; | |
r = sd_journal_get_data(j, "MESSAGE", (const void **)&d, &l); | |
if (r < 0) { | |
fprintf(stderr, "Failed to read message field: %s\n", strerror(-r)); | |
return 1; | |
} | |
struct timeval tv; | |
uint64_t curr_timestamp; | |
r = sd_journal_get_realtime_usec(j, &curr_timestamp); | |
if (r < 0) { | |
fprintf(stderr, "Failed to read timestamp: %s\n", strerror(-r)); | |
return 1; | |
} | |
else { | |
if (prev_timestamp > curr_timestamp) { | |
fprintf(stderr, "Timestamp went backwards: %llu %llu\n", prev_timestamp, curr_timestamp); | |
return 1; | |
} | |
else if (prev_timestamp < curr_timestamp) { | |
prev_timestamp = curr_timestamp; | |
} | |
tv.tv_sec = curr_timestamp / 1000000; | |
tv.tv_usec = curr_timestamp % 1000000; | |
} | |
time_t nowtime; | |
struct tm *nowtm; | |
char tmbuf[64]; | |
nowtime = tv.tv_sec; | |
nowtm = localtime(&nowtime); | |
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm); | |
printf("%s.%06ld %.*s\n", tmbuf, tv.tv_usec, (int) l, d); | |
fflush(stdout); | |
r = sd_journal_next(j); | |
} | |
// We only ever exit the above loop due to a failure calling | |
// sd_journal_next(). | |
if (r < 0) { | |
fprintf(stderr, "Failed to get next journal entry: %s\n", strerror(-r)); | |
return 1; | |
} | |
// Be kind ... close the journal via the API. | |
sd_journal_close(j); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import random | |
import string | |
import logging | |
import logging.handlers | |
log = logging.getLogger(__name__) | |
log.setLevel(logging.DEBUG) | |
handler = logging.handlers.SysLogHandler(address = '/dev/log') | |
formatter = logging.Formatter('%(module)s.%(funcName)s: %(message)s') | |
handler.setFormatter(formatter) | |
log.addHandler(handler) | |
def load(size, chars=string.ascii_uppercase + string.digits): | |
seq = 0 | |
while True: | |
msg = ''.join(random.choice(chars) for x in range(size)) | |
log.info("%20d %s" % (seq, msg)) | |
seq += 1 | |
def hello(): | |
log.debug('this is debug') | |
log.critical('this is critical') | |
if __name__ == '__main__': | |
#hello() | |
load(1024) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment