|
25 | 25 | package sdjournal |
26 | 26 |
|
27 | 27 | // #include <systemd/sd-journal.h> |
| 28 | +// #include <systemd/sd-id128.h> |
28 | 29 | // #include <stdlib.h> |
29 | 30 | // #include <syslog.h> |
30 | 31 | // |
@@ -182,6 +183,15 @@ package sdjournal |
182 | 183 | // } |
183 | 184 | // |
184 | 185 | // int |
| 186 | +// my_sd_journal_get_monotonic_usec(void *f, sd_journal *j, uint64_t *usec, sd_id128_t *boot_id) |
| 187 | +// { |
| 188 | +// int (*sd_journal_get_monotonic_usec)(sd_journal *, uint64_t *, sd_id128_t *); |
| 189 | +// |
| 190 | +// sd_journal_get_monotonic_usec = f; |
| 191 | +// return sd_journal_get_monotonic_usec(j, usec, boot_id); |
| 192 | +// } |
| 193 | +// |
| 194 | +// int |
185 | 195 | // my_sd_journal_seek_head(void *f, sd_journal *j) |
186 | 196 | // { |
187 | 197 | // int (*sd_journal_seek_head)(sd_journal *); |
@@ -227,6 +237,24 @@ package sdjournal |
227 | 237 | // return sd_journal_wait(j, timeout_usec); |
228 | 238 | // } |
229 | 239 | // |
| 240 | +// void |
| 241 | +// my_sd_journal_restart_data(void *f, sd_journal *j) |
| 242 | +// { |
| 243 | +// void (*sd_journal_restart_data)(sd_journal *); |
| 244 | +// |
| 245 | +// sd_journal_restart_data = f; |
| 246 | +// sd_journal_restart_data(j); |
| 247 | +// } |
| 248 | +// |
| 249 | +// int |
| 250 | +// my_sd_journal_enumerate_data(void *f, sd_journal *j, const void **data, size_t *length) |
| 251 | +// { |
| 252 | +// int (*sd_journal_enumerate_data)(sd_journal *, const void **, size_t *); |
| 253 | +// |
| 254 | +// sd_journal_enumerate_data = f; |
| 255 | +// return sd_journal_enumerate_data(j, data, length); |
| 256 | +// } |
| 257 | +// |
230 | 258 | import "C" |
231 | 259 | import ( |
232 | 260 | "fmt" |
@@ -288,6 +316,14 @@ type Journal struct { |
288 | 316 | lib *dlopen.LibHandle |
289 | 317 | } |
290 | 318 |
|
| 319 | +// JournalEntry represents all fields of a journal entry plus address fields. |
| 320 | +type JournalEntry struct { |
| 321 | + Fields map[string]string |
| 322 | + Cursor string |
| 323 | + RealtimeTimestamp uint64 |
| 324 | + MonotonicTimestamp uint64 |
| 325 | +} |
| 326 | + |
291 | 327 | // Match is a convenience wrapper to describe filters supplied to AddMatch. |
292 | 328 | type Match struct { |
293 | 329 | Field string |
@@ -583,6 +619,93 @@ func (j *Journal) GetDataValue(field string) (string, error) { |
583 | 619 | return strings.SplitN(val, "=", 2)[1], nil |
584 | 620 | } |
585 | 621 |
|
| 622 | +// GetEntry returns a full representation of a journal entry with |
| 623 | +// all key-value pairs of data as well as address fields (cursor, realtime |
| 624 | +// timestamp and monotonic timestamp) |
| 625 | +func (j *Journal) GetEntry() (*JournalEntry, error) { |
| 626 | + sd_journal_get_realtime_usec, err := j.getFunction("sd_journal_get_realtime_usec") |
| 627 | + if err != nil { |
| 628 | + return nil, err |
| 629 | + } |
| 630 | + |
| 631 | + sd_journal_get_monotonic_usec, err := j.getFunction("sd_journal_get_monotonic_usec") |
| 632 | + if err != nil { |
| 633 | + return nil, err |
| 634 | + } |
| 635 | + |
| 636 | + sd_journal_get_cursor, err := j.getFunction("sd_journal_get_cursor") |
| 637 | + if err != nil { |
| 638 | + return nil, err |
| 639 | + } |
| 640 | + |
| 641 | + sd_journal_restart_data, err := j.getFunction("sd_journal_restart_data") |
| 642 | + if err != nil { |
| 643 | + return nil, err |
| 644 | + } |
| 645 | + |
| 646 | + sd_journal_enumerate_data, err := j.getFunction("sd_journal_enumerate_data") |
| 647 | + if err != nil { |
| 648 | + return nil, err |
| 649 | + } |
| 650 | + |
| 651 | + j.mu.Lock() |
| 652 | + defer j.mu.Unlock() |
| 653 | + |
| 654 | + var r C.int |
| 655 | + entry := &JournalEntry{Fields: make(map[string]string)} |
| 656 | + |
| 657 | + var realtimeUsec C.uint64_t |
| 658 | + r = C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &realtimeUsec) |
| 659 | + if r < 0 { |
| 660 | + return nil, fmt.Errorf("failed to get realtime timestamp: %d", syscall.Errno(-r)) |
| 661 | + } |
| 662 | + |
| 663 | + entry.RealtimeTimestamp = uint64(realtimeUsec) |
| 664 | + |
| 665 | + var monotonicUsec C.uint64_t |
| 666 | + var boot_id C.sd_id128_t |
| 667 | + |
| 668 | + r = C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &monotonicUsec, &boot_id) |
| 669 | + if r < 0 { |
| 670 | + return nil, fmt.Errorf("failed to get monotonic timestamp: %d", syscall.Errno(-r)) |
| 671 | + } |
| 672 | + |
| 673 | + entry.MonotonicTimestamp = uint64(monotonicUsec) |
| 674 | + |
| 675 | + var c *C.char |
| 676 | + r = C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &c) |
| 677 | + if r < 0 { |
| 678 | + return nil, fmt.Errorf("failed to get cursor: %d", syscall.Errno(-r)) |
| 679 | + } |
| 680 | + |
| 681 | + entry.Cursor = C.GoString(c) |
| 682 | + |
| 683 | + // Implements the JOURNAL_FOREACH_DATA_RETVAL macro from journal-internal.h |
| 684 | + var d unsafe.Pointer |
| 685 | + var l C.size_t |
| 686 | + C.my_sd_journal_restart_data(sd_journal_restart_data, j.cjournal) |
| 687 | + for { |
| 688 | + r = C.my_sd_journal_enumerate_data(sd_journal_enumerate_data, j.cjournal, &d, &l) |
| 689 | + if r == 0 { |
| 690 | + break |
| 691 | + } |
| 692 | + |
| 693 | + if r < 0 { |
| 694 | + return nil, fmt.Errorf("failed to read message field: %d", syscall.Errno(-r)) |
| 695 | + } |
| 696 | + |
| 697 | + msg := C.GoStringN((*C.char)(d), C.int(l)) |
| 698 | + kv := strings.SplitN(msg, "=", 2) |
| 699 | + if len(kv) < 2 { |
| 700 | + return nil, fmt.Errorf("failed to parse field") |
| 701 | + } |
| 702 | + |
| 703 | + entry.Fields[kv[0]] = kv[1] |
| 704 | + } |
| 705 | + |
| 706 | + return entry, nil |
| 707 | +} |
| 708 | + |
586 | 709 | // SetDataThresold sets the data field size threshold for data returned by |
587 | 710 | // GetData. To retrieve the complete data fields this threshold should be |
588 | 711 | // turned off by setting it to 0, so that the library always returns the |
@@ -619,7 +742,28 @@ func (j *Journal) GetRealtimeUsec() (uint64, error) { |
619 | 742 | j.mu.Unlock() |
620 | 743 |
|
621 | 744 | if r < 0 { |
622 | | - return 0, fmt.Errorf("error getting timestamp for entry: %d", syscall.Errno(-r)) |
| 745 | + return 0, fmt.Errorf("failed to get realtime timestamp: %d", syscall.Errno(-r)) |
| 746 | + } |
| 747 | + |
| 748 | + return uint64(usec), nil |
| 749 | +} |
| 750 | + |
| 751 | +// GetMonotonicUsec gets the monotonic timestamp of the current journal entry. |
| 752 | +func (j *Journal) GetMonotonicUsec() (uint64, error) { |
| 753 | + var usec C.uint64_t |
| 754 | + var boot_id C.sd_id128_t |
| 755 | + |
| 756 | + sd_journal_get_monotonic_usec, err := j.getFunction("sd_journal_get_monotonic_usec") |
| 757 | + if err != nil { |
| 758 | + return 0, err |
| 759 | + } |
| 760 | + |
| 761 | + j.mu.Lock() |
| 762 | + r := C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &usec, &boot_id) |
| 763 | + j.mu.Unlock() |
| 764 | + |
| 765 | + if r < 0 { |
| 766 | + return 0, fmt.Errorf("failed to get monotonic timestamp: %d", syscall.Errno(-r)) |
623 | 767 | } |
624 | 768 |
|
625 | 769 | return uint64(usec), nil |
|
0 commit comments