1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.kubernetes;
18
19 import java.net.URL;
20 import java.nio.file.Paths;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.concurrent.locks.Lock;
24 import java.util.concurrent.locks.ReentrantLock;
25
26 import org.apache.logging.log4j.LogManager;
27 import org.apache.logging.log4j.Logger;
28 import org.apache.logging.log4j.core.LogEvent;
29 import org.apache.logging.log4j.core.config.plugins.Plugin;
30 import org.apache.logging.log4j.core.lookup.AbstractLookup;
31 import org.apache.logging.log4j.core.lookup.StrLookup;
32 import org.apache.logging.log4j.status.StatusLogger;
33 import org.apache.logging.log4j.util.LoaderUtil;
34 import org.apache.logging.log4j.util.Strings;
35
36 import io.fabric8.kubernetes.api.model.Container;
37 import io.fabric8.kubernetes.api.model.ContainerStatus;
38 import io.fabric8.kubernetes.api.model.Namespace;
39 import io.fabric8.kubernetes.api.model.Pod;
40 import io.fabric8.kubernetes.client.Config;
41 import io.fabric8.kubernetes.client.KubernetesClient;
42
43
44
45
46
47
48
49
50 @Plugin(name = "k8s", category = StrLookup.CATEGORY)
51 public class KubernetesLookup extends AbstractLookup {
52
53 private static final Logger LOGGER = StatusLogger.getLogger();
54 private static final String HOSTNAME = "HOSTNAME";
55 private static final String SPRING_ENVIRONMENT_KEY = "SpringEnvironment";
56
57 private static volatile KubernetesInfo kubernetesInfo;
58 private static final Lock initLock = new ReentrantLock();
59 private static final boolean isSpringIncluded =
60 LoaderUtil.isClassAvailable("org.apache.logging.log4j.spring.cloud.config.client.SpringEnvironmentHolder")
61 || LoaderUtil.isClassAvailable("org.apache.logging.log4j.spring.boot.SpringEnvironmentHolder");
62 private Pod pod;
63 private Namespace namespace;
64 private URL masterUrl;
65
66 public KubernetesLookup() {
67 this.pod = null;
68 this.namespace = null;
69 this.masterUrl = null;
70 initialize();
71 }
72
73 KubernetesLookup(Pod pod, Namespace namespace, URL masterUrl) {
74 this.pod = pod;
75 this.namespace = namespace;
76 this.masterUrl = masterUrl;
77 initialize();
78 }
79 private boolean initialize() {
80 if (kubernetesInfo == null || (isSpringIncluded && !kubernetesInfo.isSpringActive)) {
81 initLock.lock();
82 try {
83 boolean isSpringActive = isSpringActive();
84 if (kubernetesInfo == null || (!kubernetesInfo.isSpringActive && isSpringActive)) {
85 KubernetesInfo info = new KubernetesInfo();
86 KubernetesClient client = null;
87 info.isSpringActive = isSpringActive;
88 if (pod == null) {
89 client = new KubernetesClientBuilder().createClient();
90 if (client != null) {
91 pod = getCurrentPod(System.getenv(HOSTNAME), client);
92 info.masterUrl = client.getMasterUrl();
93 if (pod != null) {
94 info.namespace = pod.getMetadata().getNamespace();
95 namespace = client.namespaces().withName(info.namespace).get();
96 }
97 } else {
98 LOGGER.warn("Kubernetes is not available for access");
99 }
100 } else {
101 info.masterUrl = masterUrl;
102 }
103 if (pod != null) {
104 if (namespace != null) {
105 info.namespaceId = namespace.getMetadata().getUid();
106 info.namespaceAnnotations = namespace.getMetadata().getAnnotations();
107 info.namespaceLabels = namespace.getMetadata().getLabels();
108 }
109 info.app = pod.getMetadata().getLabels().get("app");
110 info.hostName = pod.getSpec().getNodeName();
111 info.annotations = pod.getMetadata().getAnnotations();
112 final String app = info.app != null ? info.app : "";
113 info.podTemplateHash = pod.getMetadata().getLabels().get("pod-template-hash");
114 info.accountName = pod.getSpec().getServiceAccountName();
115 info.clusterName = pod.getMetadata().getClusterName();
116 info.hostIp = pod.getStatus().getHostIP();
117 info.labels = pod.getMetadata().getLabels();
118 info.podId = pod.getMetadata().getUid();
119 info.podIp = pod.getStatus().getPodIP();
120 info.podName = pod.getMetadata().getName();
121 ContainerStatus containerStatus = null;
122 List<ContainerStatus> statuses = pod.getStatus().getContainerStatuses();
123 if (statuses.size() == 1) {
124 containerStatus = statuses.get(0);
125 } else if (statuses.size() > 1) {
126 String containerId = ContainerUtil.getContainerId();
127 if (containerId != null) {
128 containerStatus = statuses.stream()
129 .filter(cs -> cs.getContainerID().contains(containerId))
130 .findFirst().orElse(null);
131 }
132 }
133 final String containerName;
134 if (containerStatus != null) {
135 info.containerId = containerStatus.getContainerID();
136 info.imageId = containerStatus.getImageID();
137 containerName = containerStatus.getName();
138 } else {
139 containerName = null;
140 }
141 Container container = null;
142 List<Container> containers = pod.getSpec().getContainers();
143 if (containers.size() == 1) {
144 container = containers.get(0);
145 } else if (containers.size() > 1 && containerName != null) {
146 container = containers.stream().filter(c -> c.getName().equals(containerName))
147 .findFirst().orElse(null);
148 }
149 if (container != null) {
150 info.containerName = container.getName();
151 info.imageName = container.getImage();
152 }
153
154 kubernetesInfo = info;
155 }
156 }
157 } finally {
158 initLock.unlock();
159 }
160 }
161 return kubernetesInfo != null;
162 }
163
164 @Override
165 public String lookup(LogEvent event, String key) {
166 if (kubernetesInfo == null) {
167 return null;
168 }
169 switch (key) {
170 case "accountName": {
171 return kubernetesInfo.accountName;
172 }
173 case "annotations": {
174 return kubernetesInfo.annotations.toString();
175 }
176 case "containerId": {
177 return kubernetesInfo.containerId;
178 }
179 case "containerName": {
180 return kubernetesInfo.containerName;
181 }
182 case "clusterName": {
183 return kubernetesInfo.clusterName;
184 }
185 case "host": {
186 return kubernetesInfo.hostName;
187 }
188 case "hostIp": {
189 return kubernetesInfo.hostIp;
190 }
191 case "labels": {
192 return kubernetesInfo.labels.toString();
193 }
194 case "labels.app": {
195 return kubernetesInfo.app;
196 }
197 case "labels.podTemplateHash": {
198 return kubernetesInfo.podTemplateHash;
199 }
200 case "masterUrl": {
201 return kubernetesInfo.masterUrl.toString();
202 }
203 case "namespaceAnnotations": {
204 return kubernetesInfo.namespaceAnnotations.toString();
205 }
206 case "namespaceId": {
207 return kubernetesInfo.namespaceId;
208 }
209 case "namespaceLabels": {
210 return kubernetesInfo.namespaceLabels.toString();
211 }
212 case "namespaceName": {
213 return kubernetesInfo.namespace;
214 }
215 case "podId": {
216 return kubernetesInfo.podId;
217 }
218 case "podIp": {
219 return kubernetesInfo.podIp;
220 }
221 case "podName": {
222 return kubernetesInfo.podName;
223 }
224 case "imageId": {
225 return kubernetesInfo.imageId;
226 }
227 case "imageName": {
228 return kubernetesInfo.imageName;
229 }
230 default:
231 return null;
232 }
233 }
234
235
236
237
238 void clearInfo() {
239 kubernetesInfo = null;
240 }
241
242 private String getHostname() {
243 return System.getenv(HOSTNAME);
244 }
245
246 private Pod getCurrentPod(String hostName, KubernetesClient kubernetesClient) {
247 try {
248 if (isServiceAccount() && Strings.isNotBlank(hostName)) {
249 return kubernetesClient.pods().withName(hostName).get();
250 }
251 } catch (Throwable t) {
252 LOGGER.debug("Unable to locate pod with name {}.", hostName);
253 }
254 return null;
255 }
256
257 private boolean isServiceAccount() {
258 return Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH).toFile().exists()
259 && Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_CA_CRT_PATH).toFile().exists();
260 }
261
262 private boolean isSpringActive() {
263 return isSpringIncluded && LogManager.getFactory() != null
264 && LogManager.getFactory().hasContext(KubernetesLookup.class.getName(), null, false)
265 && LogManager.getContext(false).getObject(SPRING_ENVIRONMENT_KEY) != null;
266 }
267
268 private static class KubernetesInfo {
269 boolean isSpringActive;
270 String accountName;
271 Map<String, String> annotations;
272 String app;
273 String clusterName;
274 String containerId;
275 String containerName;
276 String hostName;
277 String hostIp;
278 String imageId;
279 String imageName;
280 Map<String, String> labels;
281 URL masterUrl;
282 String namespace;
283 Map<String, String> namespaceAnnotations;
284 String namespaceId;
285 Map<String, String> namespaceLabels;
286 String podId;
287 String podIp;
288 String podName;
289 String podTemplateHash;
290 }
291 }