blob: 56d7617199757e21a8d0713eb5253a34b4491293 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.kubernetes;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.lookup.AbstractLookup;
import org.apache.logging.log4j.core.lookup.StrLookup;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LoaderUtil;
import org.apache.logging.log4j.util.Strings;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerStatus;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
/**
* Retrieve various Kubernetes attributes. Supported keys are:
* accountName, containerId, containerName, clusterName, host, hostIp, labels, labels.app,
* labels.podTemplateHash, masterUrl, namespaceId, namespaceName, podId, podIp, podName,
* imageId, imageName.
*/
@Plugin(name = "k8s", category = StrLookup.CATEGORY)
public class KubernetesLookup extends AbstractLookup {
private static final Logger LOGGER = StatusLogger.getLogger();
private static final String HOSTNAME = "HOSTNAME";
private static final String SPRING_ENVIRONMENT_KEY = "SpringEnvironment";
private static volatile KubernetesInfo kubernetesInfo;
private static Lock initLock = new ReentrantLock();
private static boolean isSpringIncluded =
LoaderUtil.isClassAvailable("org.apache.logging.log4j.spring.cloud.config.client.SpringEnvironmentHolder");
private boolean initialize() {
if (kubernetesInfo == null || (isSpringIncluded && !kubernetesInfo.isSpringActive)) {
initLock.lock();
boolean isSpringActive = isSpringActive();
if (kubernetesInfo == null || (!kubernetesInfo.isSpringActive && isSpringActive)) {
try {
KubernetesClient client = new KubernetesClientBuilder().createClient();
if (client != null) {
KubernetesInfo info = new KubernetesInfo();
info.isSpringActive = isSpringActive;
info.hostName = getHostname();
Pod pod = getCurrentPod(info.hostName, client);
if (pod != null) {
info.app = pod.getMetadata().getLabels().get("app");
final String app = info.app != null ? info.app : "";
info.podTemplateHash = pod.getMetadata().getLabels().get("pod-template-hash");
info.accountName = pod.getSpec().getServiceAccountName();
info.clusterName = pod.getMetadata().getClusterName();
info.hostIp = pod.getStatus().getHostIP();
info.labels = pod.getMetadata().getLabels();
info.podId = pod.getMetadata().getUid();
info.podIp = pod.getStatus().getPodIP();
info.podName = pod.getMetadata().getName();
Container container = pod.getSpec().getContainers().stream()
.filter(c -> c.getName().equals(app)).findFirst().orElse(null);
if (container != null) {
info.containerName = container.getName();
info.imageName = container.getImage();
}
info.masterUrl = client.getMasterUrl();
info.namespace = pod.getMetadata().getNamespace();
Namespace namespace = client.namespaces().withName(info.namespace).get();
if (namespace != null) {
info.namespaceId = namespace.getMetadata().getUid();
}
ContainerStatus containerStatus = pod.getStatus().getContainerStatuses().stream()
.filter(cs -> cs.getName().equals(app)).findFirst().orElse(null);
if (containerStatus != null) {
info.containerId = containerStatus.getContainerID();
info.imageId = containerStatus.getImageID();
}
kubernetesInfo = info;
}
}
} finally {
initLock.unlock();
}
}
}
return kubernetesInfo != null;
}
@Override
public String lookup(LogEvent event, String key) {
if (!initialize()) {
return null;
}
switch (key) {
case "accountName": {
return kubernetesInfo.accountName;
}
case "containerId": {
return kubernetesInfo.containerId;
}
case "containerName": {
return kubernetesInfo.containerName;
}
case "clusterName": {
return kubernetesInfo.clusterName;
}
case "host": {
return kubernetesInfo.hostName;
}
case "hostIp": {
return kubernetesInfo.hostIp;
}
case "labels": {
return kubernetesInfo.labels.toString();
}
case "labels.app": {
return kubernetesInfo.app;
}
case "labels.podTemplateHash": {
return kubernetesInfo.podTemplateHash;
}
case "masterUrl": {
return kubernetesInfo.masterUrl.toString();
}
case "namespaceId": {
return kubernetesInfo.namespaceId;
}
case "namespaceName": {
return kubernetesInfo.namespace;
}
case "podId": {
return kubernetesInfo.podId;
}
case "podIp": {
return kubernetesInfo.podIp;
}
case "podName": {
return kubernetesInfo.podName;
}
case "imageId": {
return kubernetesInfo.imageId;
}
case "imageName": {
return kubernetesInfo.imageName;
}
default:
return null;
}
}
private String getHostname() {
return System.getenv(HOSTNAME);
}
private Pod getCurrentPod(String hostName, KubernetesClient kubernetesClient) {
try {
if (isServiceAccount() && Strings.isNotBlank(hostName)) {
return kubernetesClient.pods().withName(hostName).get();
}
} catch (Throwable t) {
LOGGER.debug("Unable to locate pod with name {}.", hostName);
}
return null;
}
private boolean isServiceAccount() {
return Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH).toFile().exists()
&& Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_CA_CRT_PATH).toFile().exists();
}
private boolean isSpringActive() {
return isSpringIncluded && LogManager.getFactory() != null
&& LogManager.getFactory().hasContext(KubernetesLookup.class.getName(), null, false)
&& LogManager.getContext(false).getObject(SPRING_ENVIRONMENT_KEY) != null;
}
private static class KubernetesInfo {
boolean isSpringActive;
String accountName;
String app;
String clusterName;
String containerId;
String containerName;
String hostName;
String hostIp;
String imageId;
String imageName;
Map<String, String> labels;
URL masterUrl;
String namespace;
String namespaceId;
String podId;
String podIp;
String podName;
String podTemplateHash;
}
}