Java tutorial
/** * Copyright 2016-2018 The Thingsboard Authors * * Licensed 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.thingsboard.server.dao.audit.sink; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.nio.entity.NStringEntity; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseListener; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.id.TenantId; import javax.annotation.PostConstruct; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; @Component @ConditionalOnProperty(prefix = "audit_log.sink", value = "type", havingValue = "elasticsearch") @Slf4j public class ElasticsearchAuditLogSink implements AuditLogSink { private static final String TENANT_PLACEHOLDER = "@{TENANT}"; private static final String DATE_PLACEHOLDER = "@{DATE}"; private static final String INDEX_TYPE = "audit_log"; private final ObjectMapper mapper = new ObjectMapper(); @Value("${audit_log.sink.index_pattern}") private String indexPattern; @Value("${audit_log.sink.scheme_name}") private String schemeName; @Value("${audit_log.sink.host}") private String host; @Value("${audit_log.sink.port}") private int port; @Value("${audit_log.sink.user_name}") private String userName; @Value("${audit_log.sink.password}") private String password; @Value("${audit_log.sink.date_format}") private String dateFormat; private RestClient restClient; @PostConstruct public void init() { try { log.trace("Adding elastic rest endpoint... host [{}], port [{}], scheme name [{}]", host, port, schemeName); RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, schemeName)); if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(password)) { log.trace("...using username [{}] and password ***", userName); final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password)); builder.setHttpClientConfigCallback( httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); } this.restClient = builder.build(); } catch (Exception e) { log.error("Sink init failed!", e); throw new RuntimeException(e.getMessage(), e); } } @Override public void logAction(AuditLog auditLogEntry) { String jsonContent = createElasticJsonRecord(auditLogEntry); HttpEntity entity = new NStringEntity(jsonContent, ContentType.APPLICATION_JSON); restClient.performRequestAsync(HttpMethod.POST.name(), String.format("/%s/%s", getIndexName(auditLogEntry.getTenantId()), INDEX_TYPE), Collections.emptyMap(), entity, responseListener); } private String createElasticJsonRecord(AuditLog auditLog) { ObjectNode auditLogNode = mapper.createObjectNode(); auditLogNode.put("postDate", LocalDateTime.now().toString()); auditLogNode.put("id", auditLog.getId().getId().toString()); auditLogNode.put("entityName", auditLog.getEntityName()); auditLogNode.put("tenantId", auditLog.getTenantId().getId().toString()); if (auditLog.getCustomerId() != null) { auditLogNode.put("customerId", auditLog.getCustomerId().getId().toString()); } auditLogNode.put("entityId", auditLog.getEntityId().getId().toString()); auditLogNode.put("entityType", auditLog.getEntityId().getEntityType().name()); auditLogNode.put("userId", auditLog.getUserId().getId().toString()); auditLogNode.put("userName", auditLog.getUserName()); auditLogNode.put("actionType", auditLog.getActionType().name()); if (auditLog.getActionData() != null) { auditLogNode.put("actionData", auditLog.getActionData().toString()); } auditLogNode.put("actionStatus", auditLog.getActionStatus().name()); auditLogNode.put("actionFailureDetails", auditLog.getActionFailureDetails()); return auditLogNode.toString(); } private ResponseListener responseListener = new ResponseListener() { @Override public void onSuccess(Response response) { log.trace("Elasticsearch sink log action method succeeded. Response result [{}]!", response); } @Override public void onFailure(Exception exception) { log.warn("Elasticsearch sink log action method failed!", exception); } }; private String getIndexName(TenantId tenantId) { String indexName = indexPattern; if (indexName.contains(TENANT_PLACEHOLDER) && tenantId != null) { indexName = indexName.replace(TENANT_PLACEHOLDER, tenantId.getId().toString()); } if (indexName.contains(DATE_PLACEHOLDER)) { LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat); indexName = indexName.replace(DATE_PLACEHOLDER, now.format(formatter)); } return indexName.toLowerCase(); } }