Skip to content

Commit 00b45e9

Browse files
Merge pull request #28 from ilanRosenbaum/feature/CLD-554
CLD-554: Exporting MarkLogic Logs in K8s
2 parents 76d6d92 + a5d48cd commit 00b45e9

File tree

8 files changed

+253
-8
lines changed

8 files changed

+253
-8
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ You should see an entry named "my-release" (or the release name you chose) with
179179

180180
## Configuration Options
181181

182-
This section describes the configuration options you can use with Helm. 
182+
This section describes the configuration options you can use with Helm.
183183

184184
### --values
185185

@@ -221,6 +221,15 @@ helm install my-release marklogic/marklogic --version=1.0.0-ea1 \
221221

222222
We recommend that you use the `values.yaml` file for configuring your installation.
223223

224+
### Log Collection
225+
226+
To enable log collection for all Marklogic logs set logCollection.enabled to true. Set each option in logCollection.files to true of false depending on if you want to track each type of Marklogic log file.
227+
228+
In order to use the logs that are colleceted you must define an output in the outputs section of the values file. Fluent Bit will parse and output all the log files from each pod to the output(s) you set.
229+
230+
For documentation on how to configure the Fluent Bit output with your logging backend see Fluent Bit's output documentation here: <https://docs.fluentbit.io/manual/pipeline/outputs>
231+
232+
224233
## Adding and Removing Hosts from Clusters
225234

226235
### Adding Hosts
@@ -263,7 +272,6 @@ kubectl logs pod/terminated-host-pod-name
263272
```
264273

265274
If you are permanently removing the host from the MarkLogic cluster, once the pod is terminated, follow standard MarkLogic administrative procedures using the administrative UI or APIs to remove the MarkLogic host from the cluster. Also, because Kubernetes keeps the Persistent Volume Claims and Persistent Volumes around until they are explicitly deleted, you must manually delete them using the Kubernetes APIs before attempting to scale the hosts in the StatefulSet back up again.
266-
267275
# Access the MarkLogic Server
268276

269277
## Service
@@ -412,3 +420,9 @@ This table describes the list of available parameters for Helm Chart.
412420
| `startupProbe.timeoutSeconds` | Timeout seconds for startup probe | `1` |
413421
| `startupProbe.failureThreshold` | Failure threshold for startup probe | `30` |
414422
| `startupProbe.successThreshold` | Success threshold for startup probe | `1` |
423+
| `logCollection.enabled` | Enable this parameter to enable cluster wide log collection of Marklogic server logs | `false` |
424+
| `logCollection.files.errorLogs` | Enable this parameter to enable collection of Marklogics error logs when clog collection is enabled | `true` |
425+
| `logCollection.files.accessLogs` | Enable this parameter to enable collection of Marklogics access logs when log collection is enabled | `true` |
426+
| `logCollection.files.requestLogs` | Enable this parameter to enable collection of Marklogics request logs when log collection is enabled | `true` |
427+
| `logCollection.files.crashLogs` | Enable this parameter to enable collection of Marklogics crash logs when log collection is enabled | `true` |
428+
| `logCollection.files.auditLogs` | Enable this parameter to enable collection of Marklogics audit logs when log collection is enabled | `true` |

charts/templates/configmap.yaml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,132 @@ data:
1010
MARKLOGIC_FQDN_SUFFIX: {{ include "marklogic.headlessURL" . }}
1111
MARKLOGIC_INIT: "true"
1212
MARKLOGIC_JOIN_CLUSTER: "true"
13+
---
14+
{{- if .Values.logCollection.enabled }}
15+
apiVersion: v1
16+
kind: ConfigMap
17+
metadata:
18+
name: {{ include "marklogic.fullname" . }}-fb-config-map
19+
namespace: {{ .Release.Namespace }}
20+
data:
21+
fluent-bit.conf: |
22+
[SERVICE]
23+
Flush 5
24+
Log_Level info
25+
Daemon off
26+
Parsers_File parsers.conf
27+
28+
@INCLUDE inputs.conf
29+
@INCLUDE filters.conf
30+
@INCLUDE outputs.conf
31+
32+
inputs.conf: |
33+
{{- if .Values.logCollection.files.errorLogs }}
34+
[INPUT]
35+
Name tail
36+
Path {{ .Values.persistence.mountPath }}/Logs/*ErrorLog.txt
37+
Read_from_head true
38+
Tag kube.marklogic.logs.error
39+
Path_Key path
40+
Parser error_parser
41+
Mem_Buf_Limit 4MB
42+
{{- end }}
43+
44+
{{- if .Values.logCollection.files.accessLogs }}
45+
[INPUT]
46+
Name tail
47+
Path {{ .Values.persistence.mountPath }}/Logs/*AccessLog.txt
48+
Read_from_head true
49+
tag kube.marklogic.logs.access
50+
Path_Key path
51+
Parser access_parser
52+
Mem_Buf_Limit 4MB
53+
{{- end }}
54+
55+
{{- if .Values.logCollection.files.requestLogs }}
56+
[INPUT]
57+
Name tail
58+
Path {{ .Values.persistence.mountPath }}/Logs/*RequestLog.txt
59+
Read_from_head true
60+
tag kube.marklogic.logs.request
61+
Path_Key path
62+
Parser json_parser
63+
Mem_Buf_Limit 4MB
64+
{{- end }}
65+
66+
{{- if .Values.logCollection.files.crashLogs }}
67+
[INPUT]
68+
Name tail
69+
Path {{ .Values.persistence.mountPath }}/Logs/CrashLog.txt
70+
Read_from_head true
71+
tag kube.marklogic.logs.crash
72+
Path_Key path
73+
Mem_Buf_Limit 4MB
74+
{{- end }}
75+
76+
{{- if .Values.logCollection.files.auditLogs }}
77+
[INPUT]
78+
Name tail
79+
Path {{ .Values.persistence.mountPath }}/Logs/AuditLog.txt
80+
Read_from_head true
81+
tag kube.marklogic.logs.audit
82+
Path_Key path
83+
Mem_Buf_Limit 4MB
84+
{{- end }}
85+
86+
outputs.conf: {{ toYaml .Values.logCollection.outputs | indent 2 }}
87+
88+
filters.conf: |
89+
# Enrich Logs
90+
[FILTER]
91+
Name modify
92+
Match *
93+
Add pod ${POD_NAME}
94+
Add namespace {{ .Release.Namespace }}
95+
96+
[FILTER]
97+
Name modify
98+
Match kube.marklogic.logs.error
99+
Add tag kube.marklogic.logs.error
100+
101+
[FILTER]
102+
Name modify
103+
Match kube.marklogic.logs.access
104+
Add tag kube.marklogic.logs.access
105+
106+
[FILTER]
107+
Name modify
108+
Match kube.marklogic.logs.request
109+
Add tag kube.marklogic.logs.request
110+
111+
[FILTER]
112+
Name modify
113+
Match kube.marklogic.logs.audit
114+
Add tag kube.marklogic.logs.audit
115+
116+
[FILTER]
117+
Name modify
118+
Match kube.marklogic.logs.crash
119+
Add tag kube.marklogic.logs.crash
120+
121+
parsers.conf: |
122+
[PARSER]
123+
Name error_parser
124+
Format regex
125+
Regex ^(?<time>(.+?)(?=[a-zA-Z]))(?<log_level>(.+?)(?=:))(.+?)(?=[a-zA-Z])(?<log>.*)
126+
Time_Key time
127+
Time_Format %Y-%m-%d %H:%M:%S.%L
128+
129+
[PARSER]
130+
Name access_parser
131+
Format regex
132+
Regex ^(?<host>[^ ]*)(.+?)(?<=\- )(?<user>(.+?)(?=\[))(.+?)(?<=\[)(?<time>(.+?)(?=\]))(.+?)(?<=")(?<request>[^\ ]+[^\"]+)(.+?)(?=\d)(?<response_code>[^\ ]*)(.+?)(?=\d|-)(?<response_obj_size>[^\ ]*)(.+?)(?=")(?<request_info>.*)
133+
Time_Key time
134+
Time_Format %d/%b/%Y:%H:%M:%S %z
135+
136+
[PARSER]
137+
Name json_parser
138+
Format json
139+
Time_Key time
140+
Time_Format %Y-%m-%dT%H:%M:%S%z
141+
{{- end }}

charts/templates/statefulset.yaml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,25 @@ spec:
144144
{{- with .Values.resources }}
145145
resources: {{- toYaml . | nindent 12 }}
146146
{{- end }}
147+
{{- if .Values.logCollection.enabled }}
148+
- name: fluent-bit
149+
image: {{ .Values.logCollection.image }}
150+
imagePullPolicy: IfNotPresent
151+
volumeMounts:
152+
- name: datadir
153+
mountPath: {{ .Values.persistence.mountPath }}
154+
{{- if .Values.extraVolumeMounts }}
155+
{{- toYaml .Values.extraVolumeMounts | nindent 12 }}
156+
{{- end }}
157+
- name: {{ include "marklogic.fullname" . }}-fb-config-map
158+
mountPath: /fluent-bit/etc/
159+
env:
160+
- name: POD_NAME
161+
valueFrom:
162+
fieldRef:
163+
fieldPath: metadata.name
164+
resources: {{- toYaml .Values.logCollection.resources | nindent 12 }}
165+
{{- end }}
147166
{{- with .Values.nodeSelector }}
148167
nodeSelector: {{- toYaml . | nindent 8}}
149168
{{- end }}
@@ -154,8 +173,13 @@ spec:
154173
imagePullSecrets:
155174
- name: {{ include "marklogic.fullname" . }}-registry
156175
{{- end }}
157-
{{- if or (not .Values.persistence.enabled) (.Values.extraVolumes) }}
158176
volumes:
177+
{{- if .Values.logCollection.enabled }}
178+
- name: {{ include "marklogic.fullname" . }}-fb-config-map
179+
configMap:
180+
name: {{ include "marklogic.fullname" . }}-fb-config-map
181+
{{- end }}
182+
{{- if or (not .Values.persistence.enabled) (.Values.extraVolumes) }}
159183
{{- if not .Values.persistence.enabled }}
160184
- name: datadir
161185
emptyDir: {}

charts/values.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,33 @@ startupProbe:
117117
failureThreshold: 30
118118
successThreshold: 1
119119

120+
# Configure options for log collection
121+
# Log collection will collect all logs for each file type enabled, parse them,
122+
# And export them to a logging backend specified in the outputs section below
123+
logCollection:
124+
enabled: true
125+
image: fluent/fluent-bit:1.9.7
126+
resources:
127+
requests:
128+
cpu: "100m"
129+
memory: "128Mi"
130+
limits:
131+
cpu: "100m"
132+
memory: "128Mi"
133+
files:
134+
errorLogs: true
135+
accessLogs: true
136+
requestLogs: true
137+
crashLogs: true
138+
auditLogs: true
139+
outputs: |-
140+
# Example output config for ElasticSearch output
141+
# [OUTPUT]
142+
# Name es
143+
# Match *
144+
# Host elasticsearch.default.svc.cluster.local
145+
# Port 9200
146+
# HTTP_User admin
147+
# HTTP_Passwd admin
148+
# Documentation on how to set up output can be found here: https://docs.fluentbit.io/manual/pipeline/outputs
149+
# Configure desired output below

test/e2e/clustering_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
digest_auth "github.com/xinsnake/go-http-digest-auth-client"
1616
)
1717

18-
1918
func TestClusterJoin(t *testing.T) {
2019
// Path to the helm chart we will test
2120
helmChartPath, e := filepath.Abs("../../charts")
@@ -38,6 +37,7 @@ func TestClusterJoin(t *testing.T) {
3837
"image.tag": "10-internal",
3938
"auth.adminUsername": username,
4039
"auth.adminPassword": password,
40+
"logCollection.enabled": "false",
4141
},
4242
}
4343

test/e2e/install_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestHelmInstall(t *testing.T) {
2929
"replicaCount": "1",
3030
"image.repository": "marklogic-centos/marklogic-server-centos",
3131
"image.tag": "10-internal",
32+
"logCollection.enabled": "false",
3233
},
3334
}
3435

test/e2e/upgrade_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestHelmUpgrade(t *testing.T) {
2929
"replicaCount": "1",
3030
"image.repository": "marklogic-centos/marklogic-server-centos",
3131
"image.tag": "10-internal",
32+
"logCollection.enabled": "false",
3233
},
3334
}
3435

@@ -48,6 +49,7 @@ func TestHelmUpgrade(t *testing.T) {
4849
"replicaCount": "2",
4950
"image.repository": "marklogic-centos/marklogic-server-centos",
5051
"image.tag": "10-internal",
52+
"logCollection.enabled": "false",
5153
},
5254
}
5355

test/template/template_test.go

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/gruntwork-io/terratest/modules/random"
1414
)
1515

16-
func TestChartTemplate(t *testing.T) {
16+
func TestChartTemplateNoLogCollection(t *testing.T) {
1717
t.Parallel()
1818

1919
// Path to the helm chart we will test
@@ -29,9 +29,10 @@ func TestChartTemplate(t *testing.T) {
2929
// Setup the args for helm install
3030
options := &helm.Options{
3131
SetValues: map[string]string{
32-
"image.repository": "store/marklogicdb/marklogic-server",
33-
"image.tag": "tag: 10.0-8.3-centos-1.0.0-ea3-test",
32+
"image.repository": "marklogicdb/marklogic-db",
33+
"image.tag": "latest",
3434
"persistence.enabled": "false",
35+
"logCollection.enabled": "false",
3536
},
3637
KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
3738
}
@@ -47,8 +48,52 @@ func TestChartTemplate(t *testing.T) {
4748
require.Equal(t, namespaceName, statefulset.Namespace)
4849

4950
// Verify the image matches
50-
expectedImage := "store/marklogicdb/marklogic-server:tag: 10.0-8.3-centos-1.0.0-ea3-test"
51+
expectedImage := "marklogicdb/marklogic-db:latest"
5152
statefulSetContainers := statefulset.Spec.Template.Spec.Containers
5253
require.Equal(t, len(statefulSetContainers), 1)
5354
require.Equal(t, statefulSetContainers[0].Image, expectedImage)
5455
}
56+
57+
func TestChartTemplateLogCollection(t *testing.T) {
58+
t.Parallel()
59+
60+
// Path to the helm chart we will test
61+
helmChartPath, err := filepath.Abs("../../charts")
62+
releaseName := "marklogic-test"
63+
t.Log(helmChartPath, releaseName)
64+
require.NoError(t, err)
65+
66+
// Set up the namespace; confirm that the template renders the expected value for the namespace.
67+
namespaceName := "marklogic-" + strings.ToLower(random.UniqueId())
68+
t.Logf("Namespace: %s\n", namespaceName)
69+
70+
// Setup the args for helm install
71+
options := &helm.Options{
72+
SetValues: map[string]string{
73+
"image.repository": "marklogicdb/marklogic-db",
74+
"image.tag": "latest",
75+
"persistence.enabled": "false",
76+
"logCollection.enabled": "true",
77+
},
78+
KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
79+
}
80+
81+
// render the tempate
82+
output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/statefulset.yaml"})
83+
84+
var statefulset appsv1.Deployment
85+
helm.UnmarshalK8SYaml(t, output, &statefulset)
86+
// t.Log(statefulset)
87+
88+
// Verify the name and namespace matches
89+
require.Equal(t, namespaceName, statefulset.Namespace)
90+
91+
// Verify the image matches
92+
expectedImage1 := "marklogicdb/marklogic-db:latest"
93+
expectedImage2 := "fluent/fluent-bit:1.9.7"
94+
95+
statefulSetContainers := statefulset.Spec.Template.Spec.Containers
96+
require.Equal(t, len(statefulSetContainers), 2)
97+
require.Equal(t, statefulSetContainers[0].Image, expectedImage1)
98+
require.Equal(t, statefulSetContainers[1].Image, expectedImage2)
99+
}

0 commit comments

Comments
 (0)