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.Map;
22 import java.util.concurrent.locks.Lock;
23 import java.util.concurrent.locks.ReentrantLock;
24
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.apache.logging.log4j.core.LogEvent;
28 import org.apache.logging.log4j.core.config.plugins.Plugin;
29 import org.apache.logging.log4j.core.lookup.AbstractLookup;
30 import org.apache.logging.log4j.core.lookup.StrLookup;
31 import org.apache.logging.log4j.status.StatusLogger;
32 import org.apache.logging.log4j.util.LoaderUtil;
33 import org.apache.logging.log4j.util.Strings;
34
35 import io.fabric8.kubernetes.api.model.Container;
36 import io.fabric8.kubernetes.api.model.ContainerStatus;
37 import io.fabric8.kubernetes.api.model.Namespace;
38 import io.fabric8.kubernetes.api.model.Pod;
39 import io.fabric8.kubernetes.client.Config;
40 import io.fabric8.kubernetes.client.KubernetesClient;
41
42
43
44
45
46
47
48
49 @Plugin(name = "k8s", category = StrLookup.CATEGORY)
50 public class KubernetesLookup extends AbstractLookup {
51
52 private static final Logger LOGGER = StatusLogger.getLogger();
53 private static final String HOSTNAME = "HOSTNAME";
54 private static final String SPRING_ENVIRONMENT_KEY = "SpringEnvironment";
55
56 private static volatile KubernetesInfo kubernetesInfo;
57 private static Lock initLock = new ReentrantLock();
58 private static boolean isSpringIncluded =
59 LoaderUtil.isClassAvailable("org.apache.logging.log4j.spring.cloud.config.client.SpringEnvironmentHolder");
60
61 private boolean initialize() {
62 if (kubernetesInfo == null || (isSpringIncluded && !kubernetesInfo.isSpringActive)) {
63 initLock.lock();
64 boolean isSpringActive = isSpringActive();
65 if (kubernetesInfo == null || (!kubernetesInfo.isSpringActive && isSpringActive)) {
66 try {
67 KubernetesClient client = new KubernetesClientBuilder().createClient();
68 if (client != null) {
69 KubernetesInfo info = new KubernetesInfo();
70 info.isSpringActive = isSpringActive;
71 info.hostName = getHostname();
72 Pod pod = getCurrentPod(info.hostName, client);
73 if (pod != null) {
74 info.app = pod.getMetadata().getLabels().get("app");
75 final String app = info.app != null ? info.app : "";
76 info.podTemplateHash = pod.getMetadata().getLabels().get("pod-template-hash");
77 info.accountName = pod.getSpec().getServiceAccountName();
78 info.clusterName = pod.getMetadata().getClusterName();
79 info.hostIp = pod.getStatus().getHostIP();
80 info.labels = pod.getMetadata().getLabels();
81 info.podId = pod.getMetadata().getUid();
82 info.podIp = pod.getStatus().getPodIP();
83 info.podName = pod.getMetadata().getName();
84 Container container = pod.getSpec().getContainers().stream()
85 .filter(c -> c.getName().equals(app)).findFirst().orElse(null);
86 if (container != null) {
87 info.containerName = container.getName();
88 info.imageName = container.getImage();
89 }
90 info.masterUrl = client.getMasterUrl();
91 info.namespace = pod.getMetadata().getNamespace();
92 Namespace namespace = client.namespaces().withName(info.namespace).get();
93 if (namespace != null) {
94 info.namespaceId = namespace.getMetadata().getUid();
95 }
96 ContainerStatus containerStatus = pod.getStatus().getContainerStatuses().stream()
97 .filter(cs -> cs.getName().equals(app)).findFirst().orElse(null);
98 if (containerStatus != null) {
99 info.containerId = containerStatus.getContainerID();
100 info.imageId = containerStatus.getImageID();
101 }
102 kubernetesInfo = info;
103 }
104 }
105 } finally {
106 initLock.unlock();
107 }
108 }
109 }
110 return kubernetesInfo != null;
111 }
112
113 @Override
114 public String lookup(LogEvent event, String key) {
115 if (!initialize()) {
116 return null;
117 }
118 switch (key) {
119 case "accountName": {
120 return kubernetesInfo.accountName;
121 }
122 case "containerId": {
123 return kubernetesInfo.containerId;
124 }
125 case "containerName": {
126 return kubernetesInfo.containerName;
127 }
128 case "clusterName": {
129 return kubernetesInfo.clusterName;
130 }
131 case "host": {
132 return kubernetesInfo.hostName;
133 }
134 case "hostIp": {
135 return kubernetesInfo.hostIp;
136 }
137 case "labels": {
138 return kubernetesInfo.labels.toString();
139 }
140 case "labels.app": {
141 return kubernetesInfo.app;
142 }
143 case "labels.podTemplateHash": {
144 return kubernetesInfo.podTemplateHash;
145 }
146 case "masterUrl": {
147 return kubernetesInfo.masterUrl.toString();
148 }
149 case "namespaceId": {
150 return kubernetesInfo.namespaceId;
151 }
152 case "namespaceName": {
153 return kubernetesInfo.namespace;
154 }
155 case "podId": {
156 return kubernetesInfo.podId;
157 }
158 case "podIp": {
159 return kubernetesInfo.podIp;
160 }
161 case "podName": {
162 return kubernetesInfo.podName;
163 }
164 case "imageId": {
165 return kubernetesInfo.imageId;
166 }
167 case "imageName": {
168 return kubernetesInfo.imageName;
169 }
170 default:
171 return null;
172 }
173 }
174
175 private String getHostname() {
176 return System.getenv(HOSTNAME);
177 }
178
179 private Pod getCurrentPod(String hostName, KubernetesClient kubernetesClient) {
180 try {
181 if (isServiceAccount() && Strings.isNotBlank(hostName)) {
182 return kubernetesClient.pods().withName(hostName).get();
183 }
184 } catch (Throwable t) {
185 LOGGER.debug("Unable to locate pod with name {}.", hostName);
186 }
187 return null;
188 }
189
190 private boolean isServiceAccount() {
191 return Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH).toFile().exists()
192 && Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_CA_CRT_PATH).toFile().exists();
193 }
194
195 private boolean isSpringActive() {
196 return isSpringIncluded && LogManager.getFactory() != null
197 && LogManager.getFactory().hasContext(KubernetesLookup.class.getName(), null, false)
198 && LogManager.getContext(false).getObject(SPRING_ENVIRONMENT_KEY) != null;
199 }
200
201 private static class KubernetesInfo {
202 boolean isSpringActive;
203 String accountName;
204 String app;
205 String clusterName;
206 String containerId;
207 String containerName;
208 String hostName;
209 String hostIp;
210 String imageId;
211 String imageName;
212 Map<String, String> labels;
213 URL masterUrl;
214 String namespace;
215 String namespaceId;
216 String podId;
217 String podIp;
218 String podName;
219 String podTemplateHash;
220 }
221 }