Skip to content

Commit 5af4316

Browse files
authored
Merge pull request #202 from darkonie/master
sdjournal: add GetUniqueValues
2 parents 2c9a2f6 + 7be1926 commit 5af4316

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

sdjournal/journal.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,33 @@ package sdjournal
255255
// return sd_journal_enumerate_data(j, data, length);
256256
// }
257257
//
258+
// int
259+
// my_sd_journal_query_unique(void *f, sd_journal *j, const char *field)
260+
// {
261+
// int(*sd_journal_query_unique)(sd_journal *, const char *);
262+
//
263+
// sd_journal_query_unique = f;
264+
// return sd_journal_query_unique(j, field);
265+
// }
266+
//
267+
// int
268+
// my_sd_journal_enumerate_unique(void *f, sd_journal *j, const void **data, size_t *length)
269+
// {
270+
// int(*sd_journal_enumerate_unique)(sd_journal *, const void **, size_t *);
271+
//
272+
// sd_journal_enumerate_unique = f;
273+
// return sd_journal_enumerate_unique(j, data, length);
274+
// }
275+
//
276+
// void
277+
// my_sd_journal_restart_unique(void *f, sd_journal *j)
278+
// {
279+
// void(*sd_journal_restart_unique)(sd_journal *);
280+
//
281+
// sd_journal_restart_unique = f;
282+
// sd_journal_restart_unique(j);
283+
// }
284+
//
258285
import "C"
259286
import (
260287
"bytes"
@@ -938,3 +965,60 @@ func (j *Journal) GetUsage() (uint64, error) {
938965

939966
return uint64(out), nil
940967
}
968+
969+
// GetUniqueValues returns all unique values for a given field.
970+
func (j *Journal) GetUniqueValues(field string) ([]string, error) {
971+
var result []string
972+
973+
sd_journal_query_unique, err := getFunction("sd_journal_query_unique")
974+
if err != nil {
975+
return nil, err
976+
}
977+
978+
sd_journal_enumerate_unique, err := getFunction("sd_journal_enumerate_unique")
979+
if err != nil {
980+
return nil, err
981+
}
982+
983+
sd_journal_restart_unique, err := getFunction("sd_journal_restart_unique")
984+
if err != nil {
985+
return nil, err
986+
}
987+
988+
j.mu.Lock()
989+
defer j.mu.Unlock()
990+
991+
f := C.CString(field)
992+
defer C.free(unsafe.Pointer(f))
993+
994+
r := C.my_sd_journal_query_unique(sd_journal_query_unique, j.cjournal, f)
995+
996+
if r < 0 {
997+
return nil, fmt.Errorf("failed to query journal: %d", syscall.Errno(-r))
998+
}
999+
1000+
// Implements the SD_JOURNAL_FOREACH_UNIQUE macro from sd-journal.h
1001+
var d unsafe.Pointer
1002+
var l C.size_t
1003+
C.my_sd_journal_restart_unique(sd_journal_restart_unique, j.cjournal)
1004+
for {
1005+
r = C.my_sd_journal_enumerate_unique(sd_journal_enumerate_unique, j.cjournal, &d, &l)
1006+
if r == 0 {
1007+
break
1008+
}
1009+
1010+
if r < 0 {
1011+
return nil, fmt.Errorf("failed to read message field: %d", syscall.Errno(-r))
1012+
}
1013+
1014+
msg := C.GoStringN((*C.char)(d), C.int(l))
1015+
kv := strings.SplitN(msg, "=", 2)
1016+
if len(kv) < 2 {
1017+
return nil, fmt.Errorf("failed to parse field")
1018+
}
1019+
1020+
result = append(result, kv[1])
1021+
}
1022+
1023+
return result, nil
1024+
}

sdjournal/journal_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"io"
2323
"io/ioutil"
24+
"math/rand"
2425
"os"
2526
"strings"
2627
"testing"
@@ -343,3 +344,56 @@ func TestJournalReaderSmallReadBuffer(t *testing.T) {
343344
t.Fatalf("Got unexpected message %s", got[1])
344345
}
345346
}
347+
348+
func TestJournalGetUniqueValues(t *testing.T) {
349+
j, err := NewJournal()
350+
if err != nil {
351+
t.Fatal(err)
352+
}
353+
354+
defer j.Close()
355+
356+
uniqueString := generateRandomField(20)
357+
testEntries := []string{"A", "B", "C", "D"}
358+
for _, v := range testEntries {
359+
err := journal.Send("TEST: "+uniqueString, journal.PriInfo, map[string]string{uniqueString: v})
360+
if err != nil {
361+
t.Fatal(err)
362+
}
363+
}
364+
365+
// TODO: add proper `waitOnMatch` function which should wait for journal entry with filter to commit.
366+
time.Sleep(time.Millisecond * 500)
367+
368+
values, err := j.GetUniqueValues(uniqueString)
369+
if err != nil {
370+
t.Fatal(err)
371+
}
372+
373+
if len(values) != len(testEntries) {
374+
t.Fatalf("Expect %d entries. Got %d", len(testEntries), len(values))
375+
}
376+
377+
if !contains(values, "A") || !contains(values, "B") || !contains(values, "C") || !contains(values, "D") {
378+
t.Fatalf("Expect 4 values for %s field: A,B,C,D. Got %s", uniqueString, values)
379+
}
380+
}
381+
382+
func contains(s []string, v string) bool {
383+
for _, entry := range s {
384+
if entry == v {
385+
return true
386+
}
387+
}
388+
return false
389+
}
390+
391+
func generateRandomField(n int) string {
392+
letters := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
393+
s := make([]rune, n)
394+
rand.Seed(time.Now().UnixNano())
395+
for i := range s {
396+
s[i] = letters[rand.Intn(len(letters))]
397+
}
398+
return string(s)
399+
}

0 commit comments

Comments
 (0)