Skip to content

Commit acfb302

Browse files
Merge pull request #373 from gliderlabs/master
release v3.2.4
2 parents 618a6dc + 5c4fafe commit acfb302

File tree

14 files changed

+181
-24
lines changed

14 files changed

+181
-24
lines changed

CHANGELOG.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ All notable changes to this project will be documented in this file.
1010

1111
### Changed
1212

13+
## [v3.2.4] - 2018-01-16
14+
### Fixed
15+
- @michaelshobbs fix working_directory so we don't duplicate test runs
16+
17+
### Added
18+
- @chris7444 take the hostname from /etc/host_hostname if the file is there
19+
- @chris7444 update README.md for swarm deployments PR #329
20+
- @nvanheuverzwijn strip \r and \n when reading the file /etc/host_hostname
21+
- @lucassabreu toJSON with examples
22+
23+
### Changed
24+
- @michaelshobbs pass debug to test container
25+
- @jgreat Strip header bytes from log stream
26+
- @trondvh chmod +x build.sh
27+
- @develar alpine 3.7 + golang 1.9.2
28+
1329
## [v3.2.3] - 2017-09-23
1430
### Added
1531
- @guigouz guigouz Add `RAW_FORMAT` to the documentation
@@ -162,8 +178,9 @@ All notable changes to this project will be documented in this file.
162178
- Base container is now Alpine
163179
- Moved to gliderlabs organization
164180

165-
[unreleased]: https://github.com/gliderlabs/logspout/compare/v3.2.3...HEAD
166-
[v3.2.3]: https://github.com/gliderlabs/logspout/compare/v3.2.3...v3.2.3
181+
[unreleased]: https://github.com/gliderlabs/logspout/compare/v3.2.4...HEAD
182+
[v3.2.4]: https://github.com/gliderlabs/logspout/compare/v3.2.3...v3.2.4
183+
[v3.2.3]: https://github.com/gliderlabs/logspout/compare/v3.2.2...v3.2.3
167184
[v3.2.2]: https://github.com/gliderlabs/logspout/compare/v3.2.1...v3.2.2
168185
[v3.2.1]: https://github.com/gliderlabs/logspout/compare/v3.2...v3.2.1
169186
[v3.2]: https://github.com/gliderlabs/logspout/compare/v3.1...v3.2

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine:3.5
1+
FROM alpine:3.7
22
ENTRYPOINT ["/bin/logspout"]
33
VOLUME /mnt/routes
44
EXPOSE 80
@@ -8,4 +8,4 @@ RUN cd /src && ./build.sh "$(cat VERSION)"
88

99
ONBUILD COPY ./build.sh /src/build.sh
1010
ONBUILD COPY ./modules.go /src/modules.go
11-
ONBUILD RUN cd /src && ./build.sh "$(cat VERSION)-custom"
11+
ONBUILD RUN cd /src && chmod +x ./build.sh && ./build.sh "$(cat VERSION)-custom"

Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine:3.5
1+
FROM alpine:3.6
22
VOLUME /mnt/routes
33
EXPOSE 80
44

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ test: build-dev
4949
-v /var/run/docker.sock:/var/run/docker.sock \
5050
-v $(PWD):/go/src/github.com/gliderlabs/logspout \
5151
-e TEST_ARGS="" \
52+
-e DEBUG=$(DEBUG) \
5253
$(NAME):dev make -e test-direct
5354

5455
test-direct:

README.md

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,17 @@ You can tell logspout to only include certain containers by setting filter param
5959
--volume=/var/run/docker.sock:/var/run/docker.sock \
6060
gliderlabs/logspout \
6161
raw://192.168.10.10:5000?filter.name=*_db
62-
62+
6363
$ docker run \
6464
--volume=/var/run/docker.sock:/var/run/docker.sock \
6565
gliderlabs/logspout \
6666
raw://192.168.10.10:5000?filter.id=3b6ba57db54a
67-
67+
6868
$ docker run \
6969
--volume=/var/run/docker.sock:/var/run/docker.sock \
7070
gliderlabs/logspout \
7171
raw://192.168.10.10:5000?filter.sources=stdout%2Cstderr
72-
72+
7373
# Forward logs from containers with both label 'a' starting with 'x', and label 'b' ending in 'y'.
7474
$ docker run \
7575
--volume=/var/run/docker.sock:/var/run/docker.sock \
@@ -156,6 +156,85 @@ Logspout relies on the Docker API to retrieve container logs. A failure in the A
156156
* `SYSLOG_TAG` - datum for tag field (default `{{.ContainerName}}+route.Options["append_tag"]`)
157157
* `SYSLOG_TIMESTAMP` - datum for timestamp field (default `{{.Timestamp}}`)
158158

159+
#### Raw Format
160+
161+
The raw adapter has a function `toJSON` that can be used to format the message/fields to generate JSON-like output in a simple way, or full JSON output.
162+
163+
Use examples:
164+
165+
##### Mixed JSON + generic:
166+
```
167+
{{ .Time.Format "2006-01-02T15:04:05Z07:00" }} { "container" : "{{ .Container.Name }}", "labels": {{ toJSON .Container.Config.Labels }}, "timestamp": "{{ .Time.Format "2006-01-02T15:04:05Z07:00" }}", "source" : "{{ .Source }}", "message": {{ toJSON .Data }} }
168+
```
169+
170+
```
171+
2017-10-26T11:59:32Z { "container" : "/catalogo_worker_1", "image": "sha256:e9bce6c17c80c603c4c8dbac2ad2285982d218f6ea0332f8b0fb84572941b773", "labels": {"com.docker.compose.config-hash":"4f9c3d3bfb2f65e29a4bc8a4a1b3f0a1c8a42323106a5e9106fe9279f8031321","com.docker.compose.container-number":"1","com.docker.compose.oneoff":"False","com.docker.compose.project":"catalogo","com.docker.compose.service":"worker","com.docker.compose.version":"1.16.1","logging":"true"}, "timestamp": "2017-10-26T11:59:32Z", "source" : "stdout", "message": "2017-10-26 11:59:32,950 INFO success: command_bus_0 entered RUNNING state, process has stayed up for \u003e than 1 seconds (startsecs)" }
172+
```
173+
174+
##### Full JSON like:
175+
176+
```
177+
{ "container" : "{{ .Container.Name }}", "labels": {{ toJSON .Container.Config.Labels }}, "timestamp": "{{ .Time.Format "2006-01-02T15:04:05Z07:00" }}", "source" : "{{ .Source }}", "message": {{ toJSON .Data }} }
178+
```
179+
180+
```json
181+
{
182+
"container": "/a_container",
183+
"image": "sha256:e9bce6c17c80c603c4c8dbac2ad2285982d218f6ea0332f8b0fb84572941b773",
184+
"labels": {
185+
"com.docker.compose.config-hash": "4f9c3d3bfb2f65e29a4bc8a4a1b3f0a1c8a42323106a5e9106fe9279f8031321",
186+
"com.docker.compose.container-number": "1",
187+
"com.docker.compose.oneoff": "False",
188+
"com.docker.compose.project": "a_project",
189+
"com.docker.compose.service": "worker",
190+
"com.docker.compose.version": "1.16.1",
191+
"logging": "true"
192+
},
193+
"timestamp": "2017-10-26T11:59:32Z",
194+
"source": "stdout",
195+
"message": "2017-10-26 11:59:32,950 INFO success: command_bus_0 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)"
196+
}
197+
198+
```
199+
200+
#### Using Logspout in a swarm
201+
202+
In a swarm, logspout is best deployed as a global service. When running logspout with 'docker run', you can change the value of the hostname field using the `SYSLOG_HOSTNAME` environment variable as explained above. However, this does not work in a compose file because the value for `SYSLOG_HOSTNAME` will be the same for all logspout "tasks", regardless of the docker host on which they run. To support this mode of deployment, the syslog adapter will look for the file `/etc/host_hostname` and, if the file exists and it is not empty, will configure the hostname field with the content of this file. You can then use a volume mount to map a file on the docker hosts with the file `/etc/host_hostname` in the container. The sample compose file below illustrates how this can be done
203+
204+
```
205+
version: "3"
206+
networks:
207+
logging:
208+
services:
209+
logspout:
210+
image: gliderlabs/logspout:latest
211+
networks:
212+
- logging
213+
volumes:
214+
- /etc/hostname:/etc/host_hostname:ro
215+
- /var/run/docker.sock:/var/run/docker.sock
216+
command:
217+
syslog://svt2-logger.am2.cloudra.local:514
218+
deploy:
219+
mode: global
220+
resources:
221+
limits:
222+
cpus: '0.20'
223+
memory: 256M
224+
reservations:
225+
cpus: '0.10'
226+
memory: 128M
227+
```
228+
229+
logspout can then be deployed as a global service in the swam with the following command
230+
231+
```bash
232+
docker stack deploy --compose-file <name of your compose file>
233+
```
234+
235+
More information about services and their mode of deployment can be found here:
236+
https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/
237+
159238
## Modules
160239

161240
The standard distribution of logspout comes with all modules defined in this repository. You can remove or add new modules with custom builds of logspout. In the `custom` dir, edit the `modules.go` file and do a `docker build`.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v3.2.3
1+
v3.2.4

adapters/raw/raw.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package raw
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"errors"
67
"log"
78
"net"
@@ -16,6 +17,17 @@ func init() {
1617
router.AdapterFactories.Register(NewRawAdapter, "raw")
1718
}
1819

20+
var funcs = template.FuncMap{
21+
"toJSON": func(value interface{}) string {
22+
bytes, err := json.Marshal(value)
23+
if err != nil {
24+
log.Println("error marshalling to JSON: ", err)
25+
return "null"
26+
}
27+
return string(bytes)
28+
},
29+
}
30+
1931
// NewRawAdapter returns a configured raw.Adapter
2032
func NewRawAdapter(route *router.Route) (router.LogAdapter, error) {
2133
transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
@@ -30,7 +42,7 @@ func NewRawAdapter(route *router.Route) (router.LogAdapter, error) {
3042
if os.Getenv("RAW_FORMAT") != "" {
3143
tmplStr = os.Getenv("RAW_FORMAT")
3244
}
33-
tmpl, err := template.New("raw").Parse(tmplStr)
45+
tmpl, err := template.New("raw").Funcs(funcs).Parse(tmplStr)
3446
if err != nil {
3547
return nil, err
3648
}

adapters/syslog/syslog.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7+
"io/ioutil"
78
"log"
89
"log/syslog"
910
"net"
1011
"os"
1112
"strconv"
13+
"strings"
1214
"syscall"
1315
"text/template"
1416
"time"
@@ -54,6 +56,16 @@ func debug(v ...interface{}) {
5456
}
5557
}
5658

59+
func getHostname() string {
60+
content, err := ioutil.ReadFile("/etc/host_hostname")
61+
if err == nil && len(content) > 0 {
62+
hostname = strings.TrimRight(string(content), "\r\n")
63+
} else {
64+
hostname = getopt("SYSLOG_HOSTNAME", "{{.Container.Config.Hostname}}")
65+
}
66+
return hostname
67+
}
68+
5769
// NewSyslogAdapter returnas a configured syslog.Adapter
5870
func NewSyslogAdapter(route *router.Route) (router.LogAdapter, error) {
5971
transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
@@ -67,8 +79,9 @@ func NewSyslogAdapter(route *router.Route) (router.LogAdapter, error) {
6779

6880
format := getopt("SYSLOG_FORMAT", "rfc5424")
6981
priority := getopt("SYSLOG_PRIORITY", "{{.Priority}}")
70-
hostname := getopt("SYSLOG_HOSTNAME", "{{.Container.Config.Hostname}}")
7182
pid := getopt("SYSLOG_PID", "{{.Container.State.Pid}}")
83+
hostname = getHostname()
84+
7285
tag := getopt("SYSLOG_TAG", "{{.ContainerName}}"+route.Options["append_tag"])
7386
structuredData := getopt("SYSLOG_STRUCTURED_DATA", "")
7487
if route.Options["structured_data"] != "" {

adapters/syslog/syslog_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"bufio"
55
"fmt"
66
"io"
7+
"io/ioutil"
78
"log"
89
"net"
910
"os"
1011
"strconv"
12+
"strings"
1113
"sync"
1214
"testing"
1315
"text/template"
@@ -41,6 +43,9 @@ var (
4143
}
4244
testTmplStr = fmt.Sprintf("<%s>%s %s %s[%s]: %s\n",
4345
testPriority, testTimestamp, testHostname, testTag, testPid, testData)
46+
hostHostnameFilename = "/etc/host_hostname"
47+
hostnameContent = "hostname"
48+
badHostnameContent = "hostname\r\n"
4449
)
4550

4651
func TestSyslogRetryCount(t *testing.T) {
@@ -102,6 +107,16 @@ func TestSyslogReconnectOnClose(t *testing.T) {
102107
}
103108
}
104109

110+
func TestHostnameDoesNotHaveLineFeed(t *testing.T) {
111+
if err := ioutil.WriteFile(hostHostnameFilename, []byte(badHostnameContent), 0777); err != nil {
112+
t.Fatal(err)
113+
}
114+
testHostname := getHostname()
115+
if strings.Contains(testHostname, badHostnameContent) {
116+
t.Errorf("expected hostname to be %s. got %s in hostname %s", hostnameContent, badHostnameContent, testHostname)
117+
}
118+
}
119+
105120
func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) {
106121
if n == "udp" || n == "tcp" {
107122
la = "127.0.0.1:0"

circle.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ version: 2
22
jobs:
33
build:
44
machine: true
5-
working_directory: ~/.go_workspace/src/github.com/gliderlabs/
5+
working_directory: ~/.go_workspace/src/github.com/gliderlabs/logspout
66
environment:
77
DEBUG: true
8-
GO_PROJECT: ../go/src/github.com/gliderlabs/$CIRCLE_PROJECT_REPONAME
98
steps:
109
- checkout
1110
- run: |

0 commit comments

Comments
 (0)