Documentation
Installation
Installing quicklog
Installing quicklog can be done by downloading the latest binary here or performing ‘go install’:
$ go install github.com/sheenobu/quicklog/cmd/quicklog
Configuration
quicklog provides two separate means of configuration: Loading from a file or loading from an etcd cluster.
File based configuration
File based configuration is based on JSON. The chain is described within the JSON alongside the configuration for each component.
Running with a file based configuration:
$ quicklog -filename quicklog.json
quicklog.json:
{
"input": {
"driver":"stdin"
},
"filters": [
{
"driver":"uppercase"
}
],
"output": {
"driver":"stdout"
}
}
Filters is optional, however both input and output are required.
Required and optional configuration is provided via the ‘config’ field:
{
"input": {
"driver":"nats",
"config": {
"url": "nats://127.0.0.1:4222",
"subscribe": "logging:queue"
}
}
Parsers are provided via the ‘parser’ field on ‘input’:
{
"input": {
"driver":"stdin",
"parser": "otto",
"config": {
"otto.script": "res=function(e){return l=e.split(\" \"),{message:l[0],meta:l[1]}};"
}
}
}
etcd Based Configuration
The etcd based configuration works nearly identifical to the JSON configuration. However, instead of a JSON file, each entry is stored in etcd as keys. The best way to explore this is by downloading ql2etcd, then loading a JSON based configuration into etcd:
$ go install github.com/sheenobu/quicklog/cmd/ql2etcd
$ ql2etcd -input quicklog.json -instanceName instanceName -etcdEndpoints http://localhost:4001
$ etcdctl ls --recursive
/quicklog
/quicklog/instanceName
/quicklog/instanceName/filters
/quicklog/instanceName/filters/0
/quicklog/instanceName/filters/0/driver
/quicklog/instanceName/filters/0/config
/quicklog/instanceName/reload
/quicklog/instanceName/input
/quicklog/instanceName/input/driver
/quicklog/instanceName/input/parser
/quicklog/instanceName/input/config
/quicklog/instanceName/output
/quicklog/instanceName/output/config
/quicklog/instanceName/output/driver
Each driver, parser entry is a string type. However, the ‘config’ fields are JSON types:
$ etcdctl get /quicklog/instanceName/input/config
{"otto.script":"res=function(e){return l=e.split(\" \"),{message:l[0],meta:l[1]}};"}
Running this etcd config with quicklog:
$ quicklog -instanceName instanceName -etcdEndpoints http://localhost:4001
Programmative reloading of the etcd configuration on a running quicklog instance can be
done via setting the /quicklog/instanceName/reload
field after updating the required entries.
Clustering
There has been little upfront through to clustering in quicklog. However, deploying multiple instances of quicklog can be done in many ways:
- Have each quicklog instance pull from a Nats queue, each instance getting a subset of messages which can be later merged together (via elasticsearch, bleve, etc).
- The input of one quicklog instance can be the output of another quicklog, creating a tree of quicklog instances.
- Quicklog can be embedded, made to be specialized as a specific component within a larger log aggregation architecture.
Embedding
The primary package of quicklog is github.com/sheenobu/quicklog/ql
. This package is the primary implementation of the
component system and quicklog chain. Embedding requires creating a chain, adding each required configuration option, and running the chain:
import (
"github.com/sheenobu/quicklog/filters/uuid"
"github.com/sheenobu/quicklog/inputs/stdin"
"github.com/sheenobu/quicklog/outputs/debug"
"github.com/sheenobu/quicklog/parsers/plain"
"golang.org/x/net/context"
"github.com/sheenobu/quicklog/ql"
)
func main() {
chain := ql.Chain{
Input: &stdin.Process{},
Output: &debug.Handler{PrintFields: debug.NullableBool{NotNull: false, Value: true}},
Filter: &uuid.Handler{FieldName: "uuid"},
}
ctx := context.Background()
chain.Execute(ctx)
}
This example defines our chain by importing a bunch of pre-build components. We can also provide custom components:
chain.Input = ql.InputProcessFunc(func(ctx context.Context, input chan<- ql.Buffer) error {
go func() {
for {
select {
case t := <-time.After(5 * time.Second):
input <- ql.Buffer{Data: []byte("hello + " + t.String())}
case <-ctx.Done():
return
}
}
}()
return nil
})