Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix link to coreos/go-systemd/tree/main/sdjournal #35

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ The following configuration settings are supported:

* `aws_region`: (Optional) The AWS region whose CloudWatch Logs API will be written to. If not provided,
this defaults to the region where the host EC2 instance is running.

* `ec2_instance_id`: (Optional) The id of the EC2 instance on which the tool is running. There is very
little reason to set this, since it will be automatically set to the id of the host EC2 instance.

* `journal_dir`: (Optional) Override the directory where the systemd journal can be found. This is
useful in conjunction with remote log aggregation, to work with journals synced from other systems.
The default is to use the local system's journal.

* `log_group`: (Required) The name of the cloudwatch log group to write logs into. This log group must
be created before running the program.

Expand All @@ -84,14 +84,18 @@ The following configuration settings are supported:
log level are read and pushed to CloudWatch. For more information about priority levels, look at
https://www.freedesktop.org/software/systemd/man/journalctl.html

* `log_unit`: (Optional) The `journalctl` unit to filter. By default,
not filter. Replicates the behaviour of use the command `journalctl
-u <log_unit>`. Multiple values can be provided, separated by "`,`".

* `log_stream`: (Optional) The name of the cloudwatch log stream to write logs into. This defaults to
the EC2 instance id. Each running instance of this application (along with any other applications
writing logs into the same log group) must have a unique `log_stream` value. If the given log stream
doesn't exist then it will be created before writing the first set of journal events.

* `state_file`: (Required) Path to a location where the program can write, and later read, some
state it needs to preserve between runs. (The format of this file is an implementation detail.)

* `buffer_size`: (Optional) The size of the local event buffer where journal events will be kept
in order to write batches of events to the CloudWatch Logs API. The default is 100. A batch of
new events will be written to CloudWatch Logs every second even if the buffer does not fill, but
Expand All @@ -116,7 +120,7 @@ At the time of writing, in early 2017, the supported InstanceIdentityDocument va
* `${instance.KernelID}`: The kernel ID used to launch the instance (PV instances only)
* `${instance.RamdiskID}`: The ramdisk ID used to launch the instance (PV instances only)
* `${instance.Architecture}`: The CPU architecture of the instance, eg `x86_64`

### AWS API access

This program requires access to call some of the Cloudwatch API functions. The recommended way to
Expand Down
72 changes: 35 additions & 37 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,42 @@ import (
"github.com/hashicorp/hcl"
)

type Config struct {
type config struct {
AWSCredentials *awsCredentials.Credentials
AWSRegion string
EC2InstanceId string
EC2InstanceID string
LogGroupName string
LogStreamName string
LogPriority Priority
LogPriority priorityType
LogUnit string
StateFilename string
JournalDir string
BufferSize int
}

type fileConfig struct {
AWSRegion string `hcl:"aws_region"`
EC2InstanceId string `hcl:"ec2_instance_id"`
EC2InstanceID string `hcl:"ec2_instance_id"`
LogGroupName string `hcl:"log_group"`
LogStreamName string `hcl:"log_stream"`
LogPriority string `hcl:"log_priority"`
LogUnit string `hcl:"log_unit"`
StateFilename string `hcl:"state_file"`
JournalDir string `hcl:"journal_dir"`
BufferSize int `hcl:"buffer_size"`
}

func getLogLevel(priority string) (Priority, error) {

logLevels := map[Priority][]string{
EMERGENCY: {"0", "emerg"},
ALERT: {"1", "alert"},
CRITICAL: {"2", "crit"},
ERROR: {"3", "err"},
WARNING: {"4", "warning"},
NOTICE: {"5", "notice"},
INFO: {"6", "info"},
DEBUG: {"7", "debug"},
func getLogLevel(priority string) (priorityType, error) {

logLevels := map[priorityType][]string{
emergencyP: {"0", "emerg"},
alertP: {"1", "alert"},
criticalP: {"2", "crit"},
errorP: {"3", "err"},
warningP: {"4", "warning"},
noticeP: {"5", "notice"},
infoP: {"6", "info"},
debugP: {"7", "debug"},
}

for i, s := range logLevels {
Expand All @@ -58,10 +60,10 @@ func getLogLevel(priority string) (Priority, error) {
}
}

return DEBUG, fmt.Errorf("'%s' is unsupported log priority", priority)
return debugP, fmt.Errorf("'%s' is unsupported log priority", priority)
}

func LoadConfig(filename string) (*Config, error) {
func loadConfig(filename string) (*config, error) {
configBytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
Expand All @@ -84,7 +86,7 @@ func LoadConfig(filename string) (*Config, error) {

expandFileConfig(&fConfig, metaClient)

config := &Config{}
config := &config{}

if fConfig.AWSRegion != "" {
config.AWSRegion = fConfig.AWSRegion
Expand All @@ -96,33 +98,34 @@ func LoadConfig(filename string) (*Config, error) {
config.AWSRegion = region
}

if fConfig.EC2InstanceId != "" {
config.EC2InstanceId = fConfig.EC2InstanceId
if fConfig.EC2InstanceID != "" {
config.EC2InstanceID = fConfig.EC2InstanceID
} else {
instanceId, err := metaClient.GetMetadata("instance-id")
instanceID, err := metaClient.GetMetadata("instance-id")
if err != nil {
return nil, fmt.Errorf("unable to detect EC2 instance id: %s", err)
}
config.EC2InstanceId = instanceId
config.EC2InstanceID = instanceID
}

if fConfig.LogPriority == "" {
// Log everything
config.LogPriority = DEBUG
config.LogPriority = debugP
} else {
config.LogPriority, err = getLogLevel(fConfig.LogPriority)
if err != nil {
return nil, fmt.Errorf("The provided log filtering '%s' is unsupported by systemd!", fConfig.LogPriority)
return nil, fmt.Errorf("The provided log filtering '%s' is unsupported by systemd", fConfig.LogPriority)
}
}

config.LogUnit = fConfig.LogUnit
config.LogGroupName = fConfig.LogGroupName

if fConfig.LogStreamName != "" {
config.LogStreamName = fConfig.LogStreamName
} else {
// By default we use the instance id as the stream name.
config.LogStreamName = config.EC2InstanceId
config.LogStreamName = config.EC2InstanceID
}

config.StateFilename = fConfig.StateFilename
Expand All @@ -144,7 +147,7 @@ func LoadConfig(filename string) (*Config, error) {
return config, nil
}

func (c *Config) NewAWSSession() *awsSession.Session {
func (c *config) newAWSSession() *awsSession.Session {
config := &aws.Config{
Credentials: c.AWSCredentials,
Region: aws.String(c.AWSRegion),
Expand All @@ -153,7 +156,6 @@ func (c *Config) NewAWSSession() *awsSession.Session {
return awsSession.New(config)
}


/*
* Expand variables of the form $Foo or ${Foo} in the user provided config
* from the EC2Metadata Instance Identity Document
Expand All @@ -167,12 +169,12 @@ func expandFileConfig(config *fileConfig, metaClient *ec2metadata.EC2Metadata) {
// struct extracting the string fields and their values into the vars map
data, err := metaClient.GetInstanceIdentityDocument()
if err == nil {
metadata := reflect.ValueOf( data )
metadata := reflect.ValueOf(data)

for i := 0; i < metadata.NumField(); i++ {
field := metadata.Field(i)
ftype := metadata.Type().Field(i)
if (field.Type() != reflect.TypeOf("")) {
if field.Type() != reflect.TypeOf("") {
continue
}
vars[ftype.Name] = fmt.Sprintf("%v", field.Interface())
Expand All @@ -199,8 +201,8 @@ func expandFileConfig(config *fileConfig, metaClient *ec2metadata.EC2Metadata) {
return val
}
// Unknown key => empty string
return ""
} else if (strings.HasPrefix(varname, "env.")) {
return ""
} else if strings.HasPrefix(varname, "env.") {
return os.Getenv(strings.TrimPrefix(varname, "env."))
} else {
// Unknown prefix => empty string
Expand All @@ -213,7 +215,6 @@ func expandFileConfig(config *fileConfig, metaClient *ec2metadata.EC2Metadata) {
}
}


// Modified version of os.Expand() that only expands ${name} and not $name
func expandBraceVars(s string, mapping func(string) string) string {
buf := make([]byte, 0, 2*len(s))
Expand All @@ -223,10 +224,10 @@ func expandBraceVars(s string, mapping func(string) string) string {
if s[j] == '$' && j+3 < len(s) && s[j+1] == '{' {
buf = append(buf, s[i:j]...)
idx := strings.Index(s[j+2:], "}")
if (idx >= 0) {
if idx >= 0 {
// We have a full ${name} string
buf = append(buf, mapping(s[j+2:j+2+idx])...)
j += 2+idx
j += 2 + idx
} else {
// We ran out of string (unclosed ${)
return string(buf)
Expand All @@ -236,6 +237,3 @@ func expandBraceVars(s string, mapping func(string) string) string {
}
return string(buf) + s[i:]
}



12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/boryskova/journald-cloudwatch-logs

go 1.19

require (
github.com/aws/aws-sdk-go v1.4.11-0.20160915231818-d54f7c6d021d
github.com/coreos/go-systemd v0.0.0-20160907121635-2f344660b11f
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf
github.com/go-ini/ini v1.21.1
github.com/hashicorp/hcl v0.0.0-20160902165219-99df0eb941dd
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7
)
36 changes: 36 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
github.com/aws/aws-sdk-go v1.4.11-0.20160915231818-d54f7c6d021d h1:bmLH3/SXrvYSoLHOPA0AzaD3HyAr1gL/d3TR3dzo0kA=
github.com/aws/aws-sdk-go v1.4.11-0.20160915231818-d54f7c6d021d/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k=
github.com/coreos/go-systemd v0.0.0-20160907121635-2f344660b11f h1:CjKZ1H3olidJYTH3q4CKOfR/uTuXweso6sfbOB3/CKQ=
github.com/coreos/go-systemd v0.0.0-20160907121635-2f344660b11f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ini/ini v1.21.1 h1:+QXUYsI7Tfxc64oD6R5BxU/Aq+UwGkyjH4W/hMNG7bg=
github.com/go-ini/ini v1.21.1/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hashicorp/hcl v0.0.0-20160902165219-99df0eb941dd h1:84QdurP28/GTPuAXatiouvNyhN9dflZY9KtMbatmupI=
github.com/hashicorp/hcl v0.0.0-20160902165219-99df0eb941dd/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7 h1:SMvOWPJCES2GdFracYbBQh93GXac8fq7HeN6JnpduB8=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
21 changes: 18 additions & 3 deletions journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@ package main
import (
"github.com/coreos/go-systemd/sdjournal"
"strconv"
"strings"
)

func AddLogFilters(journal *sdjournal.Journal, config *Config) {
func addLogFilters(journal *sdjournal.Journal, config *config) {

// Add Priority Filters
if config.LogPriority < DEBUG {
for p, _ := range PriorityJSON {
if config.LogPriority < debugP {
for p := range priorityJSON {
if p <= config.LogPriority {
journal.AddMatch("PRIORITY=" + strconv.Itoa(int(p)))
}
}
journal.AddDisjunction()
}

// Add unit filter (multiple values possible, separate by ",")
if config.LogUnit != "" {
unitsRaw := strings.Split(config.LogUnit, ",")

for _, unitRaw := range unitsRaw {
unit := strings.TrimSpace(unitRaw)
if unit != "" {
journal.AddMatch("SYSLOG_IDENTIFIER=" + unit)
journal.AddDisjunction()
}
}

}
}
Loading