├── .gitignore ├── src ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── com.facebook.presto.spi.Plugin │ └── java │ │ └── jp │ │ └── co │ │ └── yahoo │ │ └── presto │ │ └── audit │ │ ├── serializer │ │ ├── SerializedLog.java │ │ ├── LogSerializer.java │ │ ├── QueryIOMetadataSerializer.java │ │ ├── QueryFailureInfoSerializer.java │ │ ├── QueryCompletedEventSerializer.java │ │ ├── FullLogSerializer.java │ │ ├── QueryStatisticsSerializer.java │ │ ├── SimpleLogSerializer.java │ │ └── AuditRecord.java │ │ ├── AuditLogPlugin.java │ │ ├── AuditLogFactory.java │ │ ├── pulsar │ │ └── PulsarProducer.java │ │ ├── AuditLogListener.java │ │ ├── AuditLogFileWriter.java │ │ └── AuditConfig.java ├── license │ └── LICENSE-HEADER.txt ├── sql │ └── ddl.sql ├── test │ └── java │ │ └── jp │ │ └── co │ │ └── yahoo │ │ └── presto │ │ └── audit │ │ ├── serializer │ │ ├── TestSimpleLogSerializer.java │ │ └── TestFullLogSerializer.java │ │ ├── pulsar │ │ └── TestPulsarProducer.java │ │ ├── TestAuditConfig.java │ │ ├── TestAuditLogListener.java │ │ ├── TestHelper.java │ │ └── TestAuditLogFileWriter.java └── checkstyle │ └── checks.xml ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── .travis.yml ├── README.md ├── mvnw.cmd ├── mvnw └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | target/ 4 | **/.DS_Store 5 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.facebook.presto.spi.Plugin: -------------------------------------------------------------------------------- 1 | jp.co.yahoo.presto.audit.AuditLogPlugin 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yahoojapan/presto-audit/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | - openjdk8 5 | cache: 6 | directories: 7 | - .autoconf 8 | - $HOME/.m2 9 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip -------------------------------------------------------------------------------- /src/license/LICENSE-HEADER.txt: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/SerializedLog.java: -------------------------------------------------------------------------------- 1 | package jp.co.yahoo.presto.audit.serializer; 2 | 3 | public class SerializedLog 4 | { 5 | private String queryId; 6 | private String serializedLog; 7 | 8 | public SerializedLog(String queryId, String serializedLog) 9 | { 10 | this.queryId = queryId; 11 | this.serializedLog = serializedLog; 12 | } 13 | 14 | public String getQueryId() 15 | { 16 | return queryId; 17 | } 18 | 19 | public String getSerializedLog() 20 | { 21 | return serializedLog; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/LogSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 17 | 18 | import java.io.IOException; 19 | 20 | public interface LogSerializer 21 | { 22 | SerializedLog serialize(QueryCompletedEvent event) throws IOException; 23 | boolean shouldOutput(QueryCompletedEvent event); 24 | } 25 | -------------------------------------------------------------------------------- /src/sql/ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE presto_audit_1 ( 2 | queryId STRING, 3 | query STRING, 4 | uri STRING, 5 | state STRING, 6 | 7 | cpuTime DOUBLE, 8 | wallTime DOUBLE, 9 | queuedTime DOUBLE, 10 | peakMemoryBytes BIGINT, --Deprecated 11 | peakUserMemoryBytes BIGINT, 12 | peakTotalNonRevocableMemoryBytes BIGINT, 13 | totalBytes BIGINT, 14 | totalRows BIGINT, 15 | completedSplits INTEGER, 16 | 17 | createTime STRING, --Deprecated 18 | executionStartTime STRING, --Deprecated 19 | endTime STRING, --Decrecated 20 | 21 | createTimestamp DOUBLE, 22 | executionStartTimestamp DOUBLE, 23 | endTimestamp DOUBLE, 24 | 25 | errorCode INTEGER, 26 | errorName STRING, 27 | failureType STRING, 28 | failureMessage STRING, 29 | failuresJson STRING, 30 | 31 | remoteClientAddress STRING, 32 | clientUser STRING, 33 | userAgent STRING , 34 | source STRING 35 | ) 36 | PARTITIONED BY ( 37 | `ymd` INTEGER 38 | ) 39 | ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' 40 | ; 41 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/AuditLogPlugin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | *

6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | *

8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import com.facebook.presto.spi.Plugin; 17 | import com.facebook.presto.spi.eventlistener.EventListenerFactory; 18 | import com.google.common.collect.ImmutableList; 19 | 20 | public class AuditLogPlugin 21 | implements Plugin 22 | { 23 | @Override 24 | public Iterable getEventListenerFactories() 25 | { 26 | return ImmutableList.builder() 27 | .add(new AuditLogFactory()) 28 | .build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/QueryIOMetadataSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryIOMetadata; 17 | import com.fasterxml.jackson.core.JsonGenerator; 18 | import com.fasterxml.jackson.databind.SerializerProvider; 19 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 20 | 21 | import java.io.IOException; 22 | 23 | public class QueryIOMetadataSerializer extends StdSerializer 24 | { 25 | public QueryIOMetadataSerializer() 26 | { 27 | this(null); 28 | } 29 | 30 | public QueryIOMetadataSerializer(Class t) 31 | { 32 | super(t); 33 | } 34 | 35 | @Override 36 | public void serialize( 37 | QueryIOMetadata value, JsonGenerator jsonGenerator, SerializerProvider provider) 38 | throws IOException 39 | { 40 | jsonGenerator.writeStartObject(); 41 | jsonGenerator.writeObjectField("inputs", value.getInputs()); 42 | jsonGenerator.writeObjectField("output", value.getOutput()); 43 | jsonGenerator.writeEndObject(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/QueryFailureInfoSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryFailureInfo; 17 | import com.fasterxml.jackson.core.JsonGenerator; 18 | import com.fasterxml.jackson.databind.SerializerProvider; 19 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 20 | 21 | import java.io.IOException; 22 | 23 | public class QueryFailureInfoSerializer extends StdSerializer 24 | { 25 | public QueryFailureInfoSerializer() 26 | { 27 | this(null); 28 | } 29 | 30 | public QueryFailureInfoSerializer(Class t) 31 | { 32 | super(t); 33 | } 34 | 35 | @Override 36 | public void serialize( 37 | QueryFailureInfo value, JsonGenerator jsonGenerator, SerializerProvider provider) 38 | throws IOException 39 | { 40 | jsonGenerator.writeStartObject(); 41 | jsonGenerator.writeObjectField("errorCode", value.getErrorCode()); 42 | jsonGenerator.writeObjectField("failureType", value.getFailureType()); 43 | jsonGenerator.writeObjectField("failureMessage", value.getFailureMessage()); 44 | jsonGenerator.writeObjectField("failureTask", value.getFailureTask()); 45 | jsonGenerator.writeObjectField("failureHost", value.getFailureHost()); 46 | jsonGenerator.writeObjectField("failuresJson", value.getFailuresJson()); 47 | jsonGenerator.writeEndObject(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/AuditLogFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | *

6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | *

8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import com.facebook.presto.spi.eventlistener.EventListener; 17 | import com.facebook.presto.spi.eventlistener.EventListenerFactory; 18 | import com.google.common.base.Throwables; 19 | import com.google.inject.Injector; 20 | import com.google.inject.Scopes; 21 | import io.airlift.bootstrap.Bootstrap; 22 | 23 | import java.util.Map; 24 | 25 | import static io.airlift.configuration.ConfigBinder.configBinder; 26 | import static java.util.Objects.requireNonNull; 27 | 28 | public class AuditLogFactory 29 | implements EventListenerFactory 30 | { 31 | @Override 32 | public String getName() 33 | { 34 | return "presto-audit-log"; 35 | } 36 | 37 | @Override 38 | public EventListener create(Map config) 39 | { 40 | requireNonNull(config, "requiredConfig is null"); 41 | try { 42 | Bootstrap app = new Bootstrap( 43 | binder -> { 44 | configBinder(binder).bindConfig(AuditConfig.class); 45 | binder.bind(AuditLogListener.class).in(Scopes.SINGLETON); 46 | }); 47 | 48 | Injector injector = app 49 | .strictConfig() 50 | .doNotInitializeLogging() 51 | .setRequiredConfigurationProperties(config) 52 | .initialize(); 53 | 54 | return injector.getInstance(AuditLogListener.class); 55 | } 56 | catch (Exception e) { 57 | throw Throwables.propagate(e); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/QueryCompletedEventSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 17 | import com.fasterxml.jackson.core.JsonGenerator; 18 | import com.fasterxml.jackson.databind.SerializerProvider; 19 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 20 | 21 | import java.io.IOException; 22 | 23 | public class QueryCompletedEventSerializer extends StdSerializer 24 | { 25 | public QueryCompletedEventSerializer() 26 | { 27 | this(null); 28 | } 29 | 30 | public QueryCompletedEventSerializer(Class t) 31 | { 32 | super(t); 33 | } 34 | 35 | @Override 36 | public void serialize( 37 | QueryCompletedEvent value, JsonGenerator jsonGenerator, SerializerProvider provider) 38 | throws IOException 39 | { 40 | jsonGenerator.writeStartObject(); 41 | jsonGenerator.writeObjectField("metadata", value.getMetadata()); 42 | jsonGenerator.writeObjectField("statistics", value.getStatistics()); 43 | jsonGenerator.writeObjectField("context", value.getContext()); 44 | jsonGenerator.writeObjectField("ioMetadata", value.getIoMetadata()); 45 | jsonGenerator.writeObjectField("failureInfo", value.getFailureInfo()); 46 | jsonGenerator.writeObjectField("createTime", value.getCreateTime()); 47 | jsonGenerator.writeObjectField("executionStartTime", value.getExecutionStartTime()); 48 | jsonGenerator.writeObjectField("endTime", value.getEndTime()); 49 | jsonGenerator.writeEndObject(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/serializer/TestSimpleLogSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import jp.co.yahoo.presto.audit.TestHelper; 17 | import org.testng.annotations.Test; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | @Test(singleThreaded = true) 22 | public class TestSimpleLogSerializer 23 | { 24 | private SimpleLogSerializer simpleLogSerializer = new SimpleLogSerializer(); 25 | private TestHelper testHelper = new TestHelper(); 26 | 27 | @Test 28 | public void testSerializeNormal() 29 | { 30 | SerializedLog record = simpleLogSerializer.serialize(testHelper.createNormalEvent()); 31 | assertThat(record.getQueryId()) 32 | .matches("20170606_044544_00024_nfhe3"); 33 | assertThat(record.getSerializedLog()) 34 | .contains("\"queryId\":\"20170606_044544_00024_nfhe3\"") 35 | .contains("\"query\":\"select * from airdelays_s3_csv WHERE kw = 'presto-kw-example' limit 5\"") 36 | .contains("\"userAgent\":\"StatementClient 0.167\"") 37 | .contains("\"source\":\"presto-cli\"") 38 | .contains("\"errorCode\":0") 39 | .contains("\"createTimestamp\":1.5000804E9") 40 | .contains("\"executionStartTimestamp\":1.500080401E9") 41 | .contains("\"endTimestamp\":1.500080403E9") 42 | .contains("\"eventType\":\"QueryCompletedEvent\"") 43 | .doesNotContain("\"failureMessage\"") 44 | .doesNotContain("\"errorName\""); 45 | } 46 | 47 | @Test 48 | public void testSerializeFailure() 49 | { 50 | SerializedLog record = simpleLogSerializer.serialize(testHelper.createFailureEvent()); 51 | assertThat(record.getSerializedLog()) 52 | .contains("\"errorCode\":1") 53 | .contains("\"errorName\":\"SYNTAX_ERROR\"") 54 | .contains("\"failureMessage\":\"line 1:15: mismatched input '0' expecting ')'\"") 55 | .contains("\"failureType\":\"com.facebook.presto.sql.parser.ParsingException\""); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/FullLogSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 17 | import com.facebook.presto.spi.eventlistener.QueryFailureInfo; 18 | import com.facebook.presto.spi.eventlistener.QueryIOMetadata; 19 | import com.facebook.presto.spi.eventlistener.QueryStatistics; 20 | import com.fasterxml.jackson.core.JsonProcessingException; 21 | import com.fasterxml.jackson.databind.ObjectMapper; 22 | import com.fasterxml.jackson.databind.module.SimpleModule; 23 | import io.airlift.json.ObjectMapperProvider; 24 | 25 | import java.util.Optional; 26 | import java.util.regex.Matcher; 27 | import java.util.regex.Pattern; 28 | 29 | public class FullLogSerializer 30 | implements LogSerializer 31 | { 32 | private final ObjectMapper objectMapper; 33 | private Pattern filter; 34 | 35 | public FullLogSerializer(Optional auditLogFullFilter) 36 | { 37 | filter = auditLogFullFilter.map(Pattern::compile).orElse(null); 38 | // Initialize serializer and objectMapper 39 | SimpleModule serializerModule = new SimpleModule("presto-audit-serializer"); 40 | serializerModule.addSerializer(QueryCompletedEvent.class, new QueryCompletedEventSerializer()); 41 | serializerModule.addSerializer(QueryStatistics.class, new QueryStatisticsSerializer()); 42 | serializerModule.addSerializer(QueryIOMetadata.class, new QueryIOMetadataSerializer()); 43 | serializerModule.addSerializer(QueryFailureInfo.class, new QueryFailureInfoSerializer()); 44 | objectMapper = new ObjectMapperProvider().get().registerModule(serializerModule); 45 | } 46 | 47 | @Override 48 | public SerializedLog serialize(QueryCompletedEvent event) throws JsonProcessingException 49 | { 50 | return new SerializedLog(event.getMetadata().getQueryId(), objectMapper.writeValueAsString(event)); 51 | } 52 | 53 | @Override 54 | public boolean shouldOutput(QueryCompletedEvent event) 55 | { 56 | // Don't log if regex matches source name 57 | if (filter != null && event.getContext().getSource().isPresent()) { 58 | String source = event.getContext().getSource().get(); 59 | Matcher matcher = filter.matcher(source); 60 | return !matcher.find(); 61 | } 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/QueryStatisticsSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryStatistics; 17 | import com.fasterxml.jackson.core.JsonGenerator; 18 | import com.fasterxml.jackson.databind.SerializerProvider; 19 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 20 | 21 | import java.io.IOException; 22 | 23 | public class QueryStatisticsSerializer extends StdSerializer 24 | { 25 | public QueryStatisticsSerializer() 26 | { 27 | this(null); 28 | } 29 | 30 | public QueryStatisticsSerializer(Class t) 31 | { 32 | super(t); 33 | } 34 | 35 | @Override 36 | public void serialize( 37 | QueryStatistics value, JsonGenerator jsonGenerator, SerializerProvider provider) 38 | throws IOException 39 | { 40 | jsonGenerator.writeStartObject(); 41 | jsonGenerator.writeNumberField("cpuTime", value.getCpuTime().toMillis()); 42 | jsonGenerator.writeNumberField("wallTime", value.getWallTime().toMillis()); 43 | jsonGenerator.writeNumberField("queuedTime", value.getQueuedTime().toMillis()); 44 | if (value.getAnalysisTime().isPresent()) { 45 | jsonGenerator.writeObjectField("analysisTime", value.getAnalysisTime().get().toMillis()); 46 | } 47 | if (value.getDistributedPlanningTime().isPresent()) { 48 | jsonGenerator.writeObjectField("distributedPlanningTime", value.getDistributedPlanningTime().get().toMillis()); 49 | } 50 | jsonGenerator.writeObjectField("peakUserMemoryBytes", value.getPeakUserMemoryBytes()); 51 | jsonGenerator.writeObjectField("peakTotalNonRevocableMemoryBytes", value.getPeakTotalNonRevocableMemoryBytes()); 52 | jsonGenerator.writeObjectField("totalBytes", value.getTotalBytes()); 53 | jsonGenerator.writeObjectField("totalRows", value.getTotalRows()); 54 | jsonGenerator.writeObjectField("outputBytes", value.getOutputBytes()); 55 | jsonGenerator.writeObjectField("outputRows", value.getOutputRows()); 56 | jsonGenerator.writeObjectField("writtenBytes", value.getWrittenBytes()); 57 | jsonGenerator.writeObjectField("writtenRows", value.getWrittenRows()); 58 | jsonGenerator.writeObjectField("cumulativeMemory", value.getCumulativeMemory()); 59 | jsonGenerator.writeObjectField("completedSplits", value.getCompletedSplits()); 60 | jsonGenerator.writeObjectField("isComplete", value.isComplete()); 61 | jsonGenerator.writeObjectField("cpuTimeDistribution", value.getCpuTimeDistribution()); 62 | jsonGenerator.writeObjectField("operatorSummaries", value.getOperatorSummaries()); 63 | jsonGenerator.writeObjectField("stageGcStatistics", value.getStageGcStatistics()); 64 | jsonGenerator.writeObjectField("peakTaskTotalMemory", value.peakTaskTotalMemory()); 65 | jsonGenerator.writeEndObject(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/pulsar/TestPulsarProducer.java: -------------------------------------------------------------------------------- 1 | package jp.co.yahoo.presto.audit.pulsar; 2 | 3 | import jp.co.yahoo.presto.audit.serializer.SerializedLog; 4 | import org.apache.pulsar.client.api.ClientConfiguration; 5 | import org.apache.pulsar.client.api.CompressionType; 6 | import org.apache.pulsar.client.api.Producer; 7 | import org.apache.pulsar.client.api.ProducerConfiguration; 8 | import org.apache.pulsar.client.api.PulsarClientException; 9 | import org.apache.pulsar.client.impl.auth.AuthenticationAthenz; 10 | import org.testng.annotations.Test; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | import static org.mockito.ArgumentMatchers.any; 17 | import static org.mockito.Mockito.mock; 18 | import static org.mockito.Mockito.verify; 19 | import static org.mockito.Mockito.when; 20 | 21 | public class TestPulsarProducer 22 | { 23 | @Test 24 | public void TestClientConfigurationBuilder() 25 | throws PulsarClientException.UnsupportedAuthenticationException 26 | { 27 | Map authParams = new HashMap<>(); 28 | ClientConfiguration mockConf = mock(ClientConfiguration.class); 29 | ClientConfiguration conf = new PulsarProducer.Builder() 30 | .setUseTLS(false) 31 | .setTrustCerts("/path/to/trusted_cert") 32 | .setAuthParams(authParams) 33 | .buildClientConfiguration(mockConf); 34 | 35 | assert(mockConf == conf); 36 | verify(mockConf).setUseTls(false); 37 | verify(mockConf).setTlsTrustCertsFilePath("/path/to/trusted_cert"); 38 | verify(mockConf).setAuthentication(AuthenticationAthenz.class.getName(), authParams); 39 | } 40 | 41 | @Test 42 | public void TestProducerConfigurationBuilderDefault() 43 | { 44 | ProducerConfiguration mockConf = mock(ProducerConfiguration.class); 45 | ProducerConfiguration conf = new PulsarProducer.Builder() 46 | .buildProducerConfiguration(mockConf); 47 | 48 | assert(mockConf == conf); 49 | verify(mockConf).setSendTimeout(3, TimeUnit.SECONDS); 50 | } 51 | 52 | @Test 53 | public void TestProducerConfigurationBuilderConfig() 54 | { 55 | ProducerConfiguration mockConf = mock(ProducerConfiguration.class); 56 | ProducerConfiguration conf = new PulsarProducer.Builder() 57 | .setSendTimeout(30, TimeUnit.MILLISECONDS) 58 | .setCompressionType(CompressionType.ZLIB) 59 | .buildProducerConfiguration(mockConf); 60 | 61 | assert(mockConf == conf); 62 | verify(mockConf).setSendTimeout(30, TimeUnit.MILLISECONDS); 63 | verify(mockConf).setCompressionType(CompressionType.ZLIB); 64 | } 65 | 66 | @Test(expectedExceptions = PulsarClientException.class) 67 | public void TestBuilderFail() 68 | throws PulsarClientException 69 | { 70 | new PulsarProducer.Builder() 71 | .setURL("pulsar+ssl://pulsar.cluster.com:6651") 72 | .setTopic("persistent://namespace/global/test/topic1").build(); 73 | } 74 | 75 | @Test 76 | public void TestPulsarProducer() 77 | { 78 | Producer producer = mock(Producer.class); 79 | PulsarProducer pulsarProducer = new PulsarProducer(producer); 80 | pulsarProducer.send(new SerializedLog("queryID", "{\"a\":\"b\"}")); 81 | } 82 | 83 | @Test 84 | public void TestPulsarProducerSendError() 85 | throws PulsarClientException 86 | { 87 | Producer producer = mock(Producer.class); 88 | when(producer.send(any(byte[].class))) 89 | .thenThrow(new PulsarClientException("Mock IO Exception")); 90 | PulsarProducer pulsarProducer = new PulsarProducer(producer); 91 | pulsarProducer.send(new SerializedLog("being_dropped_queryID", "{\"a\":\"b\"}")); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 16 | # ⛔️ THIS REPOSITORY IS DEPRECATED ⛔️ 17 | This repository is no longer maintained. Please refer to the following developer guides: 18 | * https://prestodb.io/docs/current/develop/event-listener.html 19 | * https://trino.io/docs/current/develop/event-listener.html 20 | 21 | 22 | # Presto Query Audit Log(log history) 23 | Plugin for Presto to save queries and metrics into files. 24 | 25 | [![Build Status](https://travis-ci.org/yahoojapan/presto-audit.svg?branch=master)](https://travis-ci.org/yahoojapan/presto-audit) 26 | 27 | ## Requirement 28 | * Presto 0.193 or later 29 | 30 | # Build 31 | 32 | ```bash 33 | % ./mvnw clean compile package 34 | ``` 35 | 36 | ## packaging - rpm 37 | 38 | ```bash 39 | % ./mvnw clean compile package rpm:rpm 40 | ``` 41 | 42 | # Usage 43 | 44 | ## install 45 | 46 | Copy jar file to target directory. 47 | 48 | ```bash 49 | % export PLUGIN_VER=1.0 50 | % cp target/presto-audit-plugin-${PLUGIN_VER}-SNAPSHOT-all.jar /usr/lib/presto/lib/plugin/yj-audit/ 51 | ``` 52 | 53 | ## run 54 | create a event-listener.properties file under /etc/presto/ . 55 | 56 | eg. 57 | /etc/presto/event-listener.properties 58 | ```text 59 | event-listener.name=presto-audit-log 60 | event-listener.audit-log-path=/var/log/presto/ 61 | event-listener.audit-log-filename=presto-auditlog.log 62 | ``` 63 | 64 | ## full log 65 | To enable full log, add the following line to the file 66 | ```text 67 | event-listener.audit-log-full-filename=presto-auditlog-full.log 68 | ``` 69 | 70 | To add a filter for full log, add the following line to the file. 71 | The filter should be a regex. If the filter match with `--source` of a query, this query will not be 72 | logged in full log. 73 | ```text 74 | # Format: 75 | event-listener.audit-log-full-filter= 76 | 77 | # Example: 78 | event-listener.audit-log-full-filter=SRE_SYSTEM 79 | event-listener.audit-log-full-filter=(SRE_SYSTEM|Presto-team) 80 | ``` 81 | 82 | ## Send message to Pulsar broker 83 | Send the log to Pulsar broker immediately after a query is completed. 84 | Config: 85 | ```text 86 | event-listener.pulsar.simple-log-topic=persistent://namespace/global/test/topic1 87 | event-listener.pulsar.full-log-topic=persistent://namespace/global/test/topic2 88 | event-listener.pulsar.pulsar-url=pulsar+ssl://pulsar.cluster.com:6651 89 | event-listener.pulsar.pulsar-cert-path=/etc/pki/tls/certs/ca-bundle.crt 90 | event-listener.pulsar.use-TLS=true # Optional 91 | event-listener.pulsar.compression-type=ZLIB # Optional (default: NONE) 92 | event-listener.athenz.private-key-path=/etc/presto/athenz/private.key 93 | event-listener.athenz.tenant-domain=tenant.pulsar.tenant 94 | event-listener.athenz.tenant-service=mq 95 | event-listener.athenz.provider-domain=provider.plusar.tenant 96 | event-listener.athenz.config-path=/usr/local/etc/pulsar-athenz-config/athenz.conf 97 | event-listener.athenz.principal-header=Athenz-Principal-Auth 98 | event-listener.athenz.role-header=Athenz-Role-Auth 99 | ``` 100 | 101 | ## Analyze SQL samples 102 | Table DDL can be found in src/sql/ddl.sql 103 | ```sql 104 | SELECT 105 | date_parse(executionStartTime, '%Y%m%d%H%i%S.%f') AS EXEC_START, 106 | state, 107 | date_diff('millisecond', date_parse(executionStartTime, '%Y%m%d%H%i%S.%f'), date_parse(endTime, '%Y%m%d%H%i%S.%f')) AS PRESTO_EXEC_TIME_MS 108 | FROM 109 | presto_audit 110 | WHERE 111 | ymd = '20170912' AND createTime > '20170912233000.572' 112 | ORDER BY 113 | createTime; 114 | ``` 115 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/SimpleLogSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 17 | import com.facebook.presto.spi.eventlistener.QueryFailureInfo; 18 | import com.google.gson.Gson; 19 | import com.google.gson.GsonBuilder; 20 | 21 | import java.time.ZoneId; 22 | import java.time.format.DateTimeFormatter; 23 | 24 | public class SimpleLogSerializer 25 | implements LogSerializer 26 | { 27 | Gson gson; 28 | public SimpleLogSerializer() 29 | { 30 | gson = new GsonBuilder().disableHtmlEscaping().create(); 31 | } 32 | 33 | @Override 34 | public SerializedLog serialize(QueryCompletedEvent event) 35 | { 36 | AuditRecord record = buildAuditRecord(event); 37 | return new SerializedLog(event.getMetadata().getQueryId(), gson.toJson(record)); 38 | } 39 | 40 | AuditRecord buildAuditRecord(QueryCompletedEvent event) 41 | { 42 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss.SSS").withZone(ZoneId.systemDefault()); 43 | 44 | AuditRecord record = new AuditRecord(); 45 | record.setEventType("QueryCompletedEvent"); 46 | record.setQueryId(event.getMetadata().getQueryId()); 47 | 48 | //SQL Query Text 49 | record.setQuery(event.getMetadata().getQuery()); 50 | record.setUri(event.getMetadata().getUri().toString()); 51 | record.setState(event.getMetadata().getQueryState()); 52 | 53 | record.setCpuTime(event.getStatistics().getCpuTime().toMillis() / 1000.0); 54 | record.setWallTime(event.getStatistics().getWallTime().toMillis() / 1000.0); 55 | record.setQueuedTime(event.getStatistics().getQueuedTime().toMillis() / 1000.0); 56 | record.setPeakUserMemoryBytes(event.getStatistics().getPeakUserMemoryBytes()); 57 | record.setPeakTotalNonRevocableMemoryBytes(event.getStatistics().getPeakTotalNonRevocableMemoryBytes()); 58 | record.setTotalBytes(event.getStatistics().getTotalBytes()); 59 | record.setTotalRows(event.getStatistics().getTotalRows()); 60 | record.setCompletedSplits(event.getStatistics().getCompletedSplits()); 61 | 62 | record.setCreateTimestamp(event.getCreateTime().toEpochMilli() / 1000.0); 63 | record.setExecutionStartTimestamp(event.getExecutionStartTime().toEpochMilli() / 1000.0); 64 | record.setEndTimestamp(event.getEndTime().toEpochMilli() / 1000.0); 65 | 66 | // Error information 67 | if (event.getFailureInfo().isPresent()) { 68 | QueryFailureInfo failureInfo = event.getFailureInfo().get(); 69 | record.setErrorCode(failureInfo.getErrorCode().getCode()); 70 | record.setErrorName(failureInfo.getErrorCode().getName()); 71 | if (failureInfo.getFailureType().isPresent()) { 72 | record.setFailureType(failureInfo.getFailureType().get()); 73 | } 74 | if (failureInfo.getFailureMessage().isPresent()) { 75 | record.setFailureMessage(failureInfo.getFailureMessage().get()); 76 | } 77 | record.setFailuresJson(failureInfo.getFailuresJson()); 78 | } 79 | 80 | record.setRemoteClientAddress(event.getContext().getRemoteClientAddress().orElse("")); 81 | record.setClientUser(event.getContext().getUser()); 82 | record.setUserAgent(event.getContext().getUserAgent().orElse("")); 83 | record.setSource(event.getContext().getSource().orElse("")); 84 | return record; 85 | } 86 | 87 | @Override 88 | public boolean shouldOutput(QueryCompletedEvent event) 89 | { 90 | return true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/TestAuditConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | *

6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | *

8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import com.google.common.collect.ImmutableMap; 17 | import org.apache.pulsar.client.api.CompressionType; 18 | import org.testng.annotations.Test; 19 | 20 | import java.util.Map; 21 | 22 | import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; 23 | import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; 24 | import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults; 25 | 26 | public class TestAuditConfig 27 | { 28 | @Test 29 | public void testDefault() 30 | { 31 | assertRecordedDefaults(recordDefaults(AuditConfig.class) 32 | .setAuditLogPath(null) 33 | .setAuditSimpleLogName(null) 34 | .setAuditFullLogName(null) 35 | .setSimpleTopic(null) 36 | .setFullTopic(null) 37 | .setPrivateKeyPath(null) 38 | .setPulsarUrl(null) 39 | .setTrustCertsPath(null) 40 | .setLogFilter(null) 41 | .setTenantDomain(null) 42 | .setTenantService(null) 43 | .setProviderDomain(null) 44 | .setAthenzConfPath(null) 45 | .setPrincipalHeader(null) 46 | .setRoleHeader(null) 47 | .setUseTLS(true) 48 | .setCompressionType(CompressionType.NONE)); 49 | } 50 | 51 | @Test 52 | public void testExplicitConfig() 53 | { 54 | Map properties = new ImmutableMap.Builder() 55 | .put("event-listener.audit-log-path", "/var/log/presto/") 56 | .put("event-listener.audit-log-filename", "presto-audit.log") 57 | .put("event-listener.audit-log-full-filename", "presto-auditlog-full.log") 58 | .put("event-listener.audit-log-full-filter", "DROP") 59 | .put("event-listener.pulsar.simple-log-topic", "persistent://namespace/global/test/topic1") 60 | .put("event-listener.pulsar.full-log-topic", "persistent://namespace/global/test/topic2") 61 | .put("event-listener.pulsar.pulsar-url", "pulsar+ssl://pulsar.cluster.com:6651") 62 | .put("event-listener.pulsar.pulsar-cert-path", "/etc/pki/tls/certs/ca-bundle.crt") 63 | .put("event-listener.pulsar.use-TLS", "false") 64 | .put("event-listener.pulsar.compression-type", "ZLIB") 65 | .put("event-listener.athenz.private-key-path", "/etc/presto/athenz/private.key") 66 | .put("event-listener.athenz.tenant-domain", "tenant.pulsar.tenant") 67 | .put("event-listener.athenz.tenant-service", "mq") 68 | .put("event-listener.athenz.provider-domain", "provider.plusar.tenant") 69 | .put("event-listener.athenz.config-path", "/usr/local/etc/pulsar-athenz-config/athenz.conf") 70 | .put("event-listener.athenz.principal-header", "Athenz-Principal-Auth") 71 | .put("event-listener.athenz.role-header", "Athenz-Role-Auth") 72 | .build(); 73 | 74 | AuditConfig expected = new AuditConfig() 75 | .setAuditLogPath("/var/log/presto/") 76 | .setAuditSimpleLogName("presto-audit.log") 77 | .setAuditFullLogName("presto-auditlog-full.log") 78 | .setSimpleTopic("persistent://namespace/global/test/topic1") 79 | .setFullTopic("persistent://namespace/global/test/topic2") 80 | .setPrivateKeyPath("/etc/presto/athenz/private.key") 81 | .setPulsarUrl("pulsar+ssl://pulsar.cluster.com:6651") 82 | .setUseTLS(false) 83 | .setCompressionType(CompressionType.ZLIB) 84 | .setTrustCertsPath("/etc/pki/tls/certs/ca-bundle.crt") 85 | .setLogFilter("DROP") 86 | .setTenantDomain("tenant.pulsar.tenant") 87 | .setTenantService("mq") 88 | .setProviderDomain("provider.plusar.tenant") 89 | .setAthenzConfPath("/usr/local/etc/pulsar-athenz-config/athenz.conf") 90 | .setPrincipalHeader("Athenz-Principal-Auth") 91 | .setRoleHeader("Athenz-Role-Auth"); 92 | 93 | assertFullMapping(properties, expected); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.net.*; 21 | import java.io.*; 22 | import java.nio.channels.*; 23 | import java.util.Properties; 24 | 25 | public class MavenWrapperDownloader { 26 | 27 | /** 28 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 29 | */ 30 | private static final String DEFAULT_DOWNLOAD_URL = 31 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar"; 32 | 33 | /** 34 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 35 | * use instead of the default one. 36 | */ 37 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 38 | ".mvn/wrapper/maven-wrapper.properties"; 39 | 40 | /** 41 | * Path where the maven-wrapper.jar will be saved to. 42 | */ 43 | private static final String MAVEN_WRAPPER_JAR_PATH = 44 | ".mvn/wrapper/maven-wrapper.jar"; 45 | 46 | /** 47 | * Name of the property which should be used to override the default download url for the wrapper. 48 | */ 49 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 50 | 51 | public static void main(String args[]) { 52 | System.out.println("- Downloader started"); 53 | File baseDirectory = new File(args[0]); 54 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 55 | 56 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 57 | // wrapperUrl parameter. 58 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 59 | String url = DEFAULT_DOWNLOAD_URL; 60 | if(mavenWrapperPropertyFile.exists()) { 61 | FileInputStream mavenWrapperPropertyFileInputStream = null; 62 | try { 63 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 64 | Properties mavenWrapperProperties = new Properties(); 65 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 66 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 67 | } catch (IOException e) { 68 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 69 | } finally { 70 | try { 71 | if(mavenWrapperPropertyFileInputStream != null) { 72 | mavenWrapperPropertyFileInputStream.close(); 73 | } 74 | } catch (IOException e) { 75 | // Ignore ... 76 | } 77 | } 78 | } 79 | System.out.println("- Downloading from: : " + url); 80 | 81 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 82 | if(!outputFile.getParentFile().exists()) { 83 | if(!outputFile.getParentFile().mkdirs()) { 84 | System.out.println( 85 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 86 | } 87 | } 88 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 89 | try { 90 | downloadFileFromURL(url, outputFile); 91 | System.out.println("Done"); 92 | System.exit(0); 93 | } catch (Throwable e) { 94 | System.out.println("- Error downloading"); 95 | e.printStackTrace(); 96 | System.exit(1); 97 | } 98 | } 99 | 100 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 101 | URL website = new URL(urlString); 102 | ReadableByteChannel rbc; 103 | rbc = Channels.newChannel(website.openStream()); 104 | FileOutputStream fos = new FileOutputStream(destination); 105 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 106 | fos.close(); 107 | rbc.close(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/serializer/TestFullLogSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | /* 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | package jp.co.yahoo.presto.audit.serializer; 28 | 29 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 30 | import com.fasterxml.jackson.core.JsonProcessingException; 31 | import jp.co.yahoo.presto.audit.TestHelper; 32 | import org.testng.annotations.DataProvider; 33 | import org.testng.annotations.Test; 34 | 35 | import java.util.Optional; 36 | 37 | import static org.assertj.core.api.Assertions.assertThat; 38 | import static org.testng.Assert.assertEquals; 39 | 40 | @Test(singleThreaded = true) 41 | public class TestFullLogSerializer 42 | { 43 | private FullLogSerializer fullLogSerializer = new FullLogSerializer(Optional.empty()); 44 | private TestHelper testHelper = new TestHelper(); 45 | 46 | @Test 47 | public void testSerializeNormal() throws JsonProcessingException 48 | { 49 | SerializedLog record = fullLogSerializer.serialize(testHelper.createNormalEvent()); 50 | assertThat(record.getQueryId()) 51 | .matches("20170606_044544_00024_nfhe3"); 52 | assertThat(record.getSerializedLog()) 53 | .contains("\"queryId\":\"20170606_044544_00024_nfhe3\"") 54 | .contains("\"query\":\"select * from airdelays_s3_csv WHERE kw = 'presto-kw-example' limit 5\"") 55 | .contains("\"userAgent\":\"StatementClient 0.167\"") 56 | .contains("\"source\":\"presto-cli\"") 57 | .contains("\"createTime\":\"2017-07-15T01:00:00Z\"") 58 | .contains("\"executionStartTime\":\"2017-07-15T01:00:01Z\"") 59 | .doesNotContain("\"failureMessage\"") 60 | .doesNotContain("\"code\""); 61 | } 62 | 63 | @Test 64 | public void testSerializeFailure() throws JsonProcessingException 65 | { 66 | SerializedLog record = fullLogSerializer.serialize(testHelper.createFailureEvent()); 67 | assertThat(record.getSerializedLog()) 68 | .contains("\"failureInfo\"") 69 | .contains("\"code\":1") 70 | .contains("\"name\":\"SYNTAX_ERROR\"") 71 | .contains("\"failureMessage\":\"line 1:15: mismatched input '0' expecting ')'\"") 72 | .contains("\"failureType\":\"com.facebook.presto.sql.parser.ParsingException\""); 73 | } 74 | 75 | @DataProvider(name="filter-provider") 76 | public Object[][] testData(){ 77 | String filter1 = "sre_system"; 78 | String filter2 = "(sre_system|presto-cli)"; 79 | return new Object[][]{ 80 | // No filter 81 | {Optional.empty(), Optional.empty(), true}, 82 | {Optional.empty(), Optional.of("presto-cli"), true}, 83 | {Optional.empty(), Optional.of("sre_system"), true}, 84 | // Filter1 85 | {Optional.of(filter1), Optional.empty(), true}, 86 | {Optional.of(filter1), Optional.of("presto-cli"), true}, 87 | {Optional.of(filter1), Optional.of("sre_system"), false}, 88 | // Filter2 89 | {Optional.of(filter2), Optional.empty(), true}, 90 | {Optional.of(filter2), Optional.of("presto-cli"), false}, 91 | {Optional.of(filter2), Optional.of("sre_system"), false} 92 | }; 93 | } 94 | 95 | @Test(dataProvider = "filter-provider") 96 | public void testShouldOutput(Optional filter, Optional source, boolean result) { 97 | FullLogSerializer serializer = new FullLogSerializer(filter); 98 | QueryCompletedEvent event = testHelper.createQueryWithSource(source); 99 | boolean shouldOutput = serializer.shouldOutput(event); 100 | assertEquals(shouldOutput, result); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/pulsar/PulsarProducer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | *

6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | *

8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.pulsar; 15 | 16 | import com.google.common.annotations.VisibleForTesting; 17 | import io.airlift.log.Logger; 18 | import jp.co.yahoo.presto.audit.serializer.SerializedLog; 19 | import org.apache.pulsar.client.api.ClientConfiguration; 20 | import org.apache.pulsar.client.api.CompressionType; 21 | import org.apache.pulsar.client.api.Producer; 22 | import org.apache.pulsar.client.api.ProducerConfiguration; 23 | import org.apache.pulsar.client.api.PulsarClient; 24 | import org.apache.pulsar.client.api.PulsarClientException; 25 | import org.apache.pulsar.client.impl.auth.AuthenticationAthenz; 26 | 27 | import java.util.Map; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | public class PulsarProducer 31 | { 32 | private static final Logger log = Logger.get(PulsarProducer.class); 33 | private Producer producer; 34 | 35 | @VisibleForTesting 36 | public PulsarProducer(Producer producer) 37 | { 38 | this.producer = producer; 39 | } 40 | 41 | public void send(SerializedLog message) 42 | { 43 | try { 44 | producer.send(message.getSerializedLog().getBytes()); 45 | } 46 | catch (PulsarClientException e) { 47 | log.error("Failed to send message to Pulsar broker. " + e); 48 | log.error("Dropped queryID: " + message.getQueryId()); 49 | } 50 | } 51 | 52 | public static class Builder 53 | { 54 | private String topic; 55 | private String url; 56 | private String trustCerts; 57 | private Map authParams; 58 | private boolean useTLS; 59 | private int sendTimeout = 3; 60 | private TimeUnit sendTimeoutUnit = TimeUnit.SECONDS; 61 | private CompressionType compressionType = CompressionType.NONE; 62 | 63 | public Builder setTopic(String topic) 64 | { 65 | this.topic = topic; 66 | return this; 67 | } 68 | 69 | public Builder setURL(String url) 70 | { 71 | this.url = url; 72 | return this; 73 | } 74 | 75 | public Builder setTrustCerts(String trustCerts) 76 | { 77 | this.trustCerts = trustCerts; 78 | return this; 79 | } 80 | 81 | public Builder setAuthParams(Map authParams) 82 | { 83 | this.authParams = authParams; 84 | return this; 85 | } 86 | 87 | public Builder setUseTLS(boolean useTLS) 88 | { 89 | this.useTLS = useTLS; 90 | return this; 91 | } 92 | 93 | public Builder setSendTimeout(int sendTimeout, TimeUnit timeUnit) 94 | { 95 | this.sendTimeout = sendTimeout; 96 | this.sendTimeoutUnit = timeUnit; 97 | return this; 98 | } 99 | 100 | public Builder setCompressionType(CompressionType compressionType) 101 | { 102 | this.compressionType = compressionType; 103 | return this; 104 | } 105 | 106 | @VisibleForTesting 107 | ClientConfiguration buildClientConfiguration(ClientConfiguration conf) 108 | throws PulsarClientException.UnsupportedAuthenticationException 109 | { 110 | // Config PulsarClient 111 | conf.setUseTls(useTLS); 112 | conf.setTlsTrustCertsFilePath(trustCerts); 113 | conf.setAuthentication(AuthenticationAthenz.class.getName(), authParams); 114 | return conf; 115 | } 116 | 117 | @VisibleForTesting 118 | ProducerConfiguration buildProducerConfiguration(ProducerConfiguration prodConf) 119 | { 120 | prodConf.setSendTimeout(sendTimeout, sendTimeoutUnit); 121 | prodConf.setCompressionType(compressionType); 122 | return prodConf; 123 | } 124 | 125 | public PulsarProducer build() 126 | throws PulsarClientException 127 | { 128 | ClientConfiguration conf = buildClientConfiguration(new ClientConfiguration()); 129 | ProducerConfiguration prodConf = buildProducerConfiguration(new ProducerConfiguration()); 130 | PulsarClient pulsarClient = PulsarClient.create(url, conf); 131 | Producer producer = pulsarClient.createProducer(topic, prodConf); 132 | return new PulsarProducer(producer); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/AuditLogListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import com.facebook.presto.spi.eventlistener.EventListener; 17 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 18 | import com.facebook.presto.spi.eventlistener.QueryCreatedEvent; 19 | import com.fasterxml.jackson.core.JsonProcessingException; 20 | import io.airlift.log.Logger; 21 | import jp.co.yahoo.presto.audit.pulsar.PulsarProducer; 22 | import jp.co.yahoo.presto.audit.serializer.FullLogSerializer; 23 | import jp.co.yahoo.presto.audit.serializer.SerializedLog; 24 | import jp.co.yahoo.presto.audit.serializer.SimpleLogSerializer; 25 | import org.apache.pulsar.client.api.PulsarClientException; 26 | 27 | import javax.inject.Inject; 28 | 29 | import java.io.File; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | import java.util.Optional; 33 | 34 | import static java.util.Objects.requireNonNull; 35 | 36 | public class AuditLogListener 37 | implements EventListener 38 | { 39 | private static final Logger log = Logger.get(AuditLogListener.class); 40 | private final AuditLogFileWriter auditLogWriter; 41 | private final String simpleLogFilePath; 42 | private final Optional fullLogFilePath; 43 | private final FullLogSerializer fullLogSerializer; 44 | private final SimpleLogSerializer simpleLogSerializer; 45 | private PulsarProducer pulsarSimpleProducer = null; 46 | private PulsarProducer pulsarFullProducer = null; 47 | 48 | @Inject 49 | public AuditLogListener(AuditConfig auditConfig) 50 | throws PulsarClientException 51 | { 52 | this.auditLogWriter = auditConfig.getAuditLogFileWriter(); 53 | 54 | // File logger setting 55 | String auditLogPath = requireNonNull(auditConfig.getAuditLogPath(), "auditLogPath is null"); 56 | String simpleLogName = requireNonNull(auditConfig.getAuditSimpleLogName(), "simpleLogName is null"); 57 | Optional fullLogName = Optional.ofNullable(auditConfig.getAuditFullLogName()); 58 | this.simpleLogFilePath = auditLogPath + File.separator + simpleLogName; 59 | this.fullLogFilePath = fullLogName.map(s -> auditLogPath + File.separator + s); 60 | Optional auditLogFullFilter = Optional.ofNullable(auditConfig.getLogFilter()); 61 | fullLogSerializer = new FullLogSerializer(auditLogFullFilter); 62 | simpleLogSerializer = new SimpleLogSerializer(); 63 | 64 | // Pulsar setting 65 | Optional simpleLogTopic = Optional.ofNullable(auditConfig.getSimpleTopic()); 66 | Optional fullLogTopic = Optional.ofNullable(auditConfig.getFullTopic()); 67 | if (simpleLogTopic.isPresent() || fullLogTopic.isPresent()) { 68 | Map authParams = new HashMap<>(); 69 | authParams.put("tenantDomain", requireNonNull(auditConfig.getTenantDomain())); 70 | authParams.put("tenantService", requireNonNull(auditConfig.getTenantService())); 71 | authParams.put("providerDomain", requireNonNull(auditConfig.getProviderDomain())); 72 | authParams.put("privateKey", requireNonNull(auditConfig.getPrivateKeyPath())); 73 | authParams.put("athenzConfPath", requireNonNull(auditConfig.getAthenzConfPath())); 74 | authParams.put("principalHeader", requireNonNull(auditConfig.getPrincipalHeader())); 75 | authParams.put("roleHeader", requireNonNull(auditConfig.getRoleHeader())); 76 | 77 | PulsarProducer.Builder builder = new PulsarProducer.Builder() 78 | .setURL(requireNonNull(auditConfig.getPulsarUrl())) 79 | .setTrustCerts(requireNonNull(auditConfig.getTrustCertsPath())) 80 | .setAuthParams(authParams) 81 | .setCompressionType(auditConfig.getCompressionType()) 82 | .setUseTLS(auditConfig.getUseTLS()); 83 | 84 | if (simpleLogTopic.isPresent()) { 85 | pulsarSimpleProducer = builder.setTopic(simpleLogTopic.get()).build(); 86 | } 87 | 88 | if (fullLogTopic.isPresent()) { 89 | pulsarFullProducer = builder.setTopic(fullLogTopic.get()).build(); 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public void queryCreated(QueryCreatedEvent queryCreatedEvent) 96 | { 97 | log.debug("QUERY SQL : [ %s ]", queryCreatedEvent.getMetadata().getQuery()); 98 | } 99 | 100 | @Override 101 | public void queryCompleted(QueryCompletedEvent queryCompletedEvent) 102 | { 103 | simpleLog(queryCompletedEvent); 104 | fullLog(queryCompletedEvent); 105 | } 106 | 107 | private void simpleLog(QueryCompletedEvent queryCompletedEvent) 108 | { 109 | SerializedLog simpleLog = simpleLogSerializer.serialize(queryCompletedEvent); 110 | auditLogWriter.write(simpleLogFilePath, simpleLog); 111 | if (pulsarSimpleProducer != null) { 112 | pulsarSimpleProducer.send(simpleLog); 113 | } 114 | } 115 | 116 | private void fullLog(QueryCompletedEvent queryCompletedEvent) 117 | { 118 | if (fullLogFilePath.isPresent() && fullLogSerializer.shouldOutput(queryCompletedEvent)) { 119 | try { 120 | SerializedLog fullLog = fullLogSerializer.serialize(queryCompletedEvent); 121 | auditLogWriter.write(fullLogFilePath.get(), fullLog); 122 | if (pulsarFullProducer != null) { 123 | pulsarFullProducer.send(fullLog); 124 | } 125 | } 126 | catch (JsonProcessingException e) { 127 | log.error("Error in serializing full audit log: " + e.getMessage()); 128 | log.error("Query failed: " + queryCompletedEvent.getMetadata().getQueryId()); 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/AuditLogFileWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import com.google.common.annotations.VisibleForTesting; 17 | import com.google.common.cache.CacheBuilder; 18 | import com.google.common.cache.CacheLoader; 19 | import com.google.common.cache.LoadingCache; 20 | import com.google.common.cache.RemovalListener; 21 | import io.airlift.log.Logger; 22 | import jp.co.yahoo.presto.audit.serializer.SerializedLog; 23 | 24 | import java.io.FileWriter; 25 | import java.io.IOException; 26 | import java.util.AbstractMap; 27 | import java.util.Map; 28 | import java.util.concurrent.ArrayBlockingQueue; 29 | import java.util.concurrent.BlockingQueue; 30 | import java.util.concurrent.TimeUnit; 31 | 32 | public class AuditLogFileWriter 33 | implements Runnable 34 | { 35 | private static final int QUEUE_CAPACITY = 10000; 36 | private static final int FILE_TIMEOUT_SEC = 3; 37 | 38 | private static Logger log = Logger.get(AuditLogFileWriter.class); 39 | private static AuditLogFileWriter singleton; 40 | private final Thread t; 41 | 42 | private volatile boolean isTerminate = false; 43 | private final BlockingQueue> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); 44 | private LoadingCache fileWriters; 45 | 46 | @VisibleForTesting 47 | private AuditLogFileWriter(WriterFactory writerFactory, Logger logger) 48 | { 49 | this(writerFactory); 50 | log = logger; 51 | } 52 | 53 | private AuditLogFileWriter(WriterFactory writerFactory) 54 | { 55 | t = new Thread(this, "AuditLogWriter Thread"); 56 | 57 | // Close file handler when cache timeout 58 | RemovalListener removalListener = removal -> { 59 | FileWriter h = removal.getValue(); 60 | try { 61 | log.debug("Close FileWriter: " + removal.getKey()); 62 | h.close(); 63 | } 64 | catch (Exception e) { 65 | log.error("Failed to close file: " + removal.getKey()); 66 | } 67 | }; 68 | 69 | // Open file handler when cache is needed 70 | fileWriters = CacheBuilder.newBuilder() 71 | .expireAfterWrite(FILE_TIMEOUT_SEC, TimeUnit.SECONDS) 72 | .removalListener(removalListener) 73 | .build(new CacheLoader() 74 | { 75 | public FileWriter load(String filename) 76 | throws IOException 77 | { 78 | try { 79 | log.debug("Open new FileWriter: " + filename); 80 | return writerFactory.getFileWriter(filename); 81 | } 82 | catch (Exception e) { 83 | log.error("Failed to open file: " + e.getMessage()); 84 | throw e; 85 | } 86 | } 87 | }); 88 | } 89 | 90 | /** 91 | * Return the singleton instance for this class 92 | * 93 | * @return singleton instance 94 | */ 95 | static synchronized AuditLogFileWriter getInstance() 96 | { 97 | if (singleton == null) { 98 | singleton = new AuditLogFileWriter(new WriterFactory()); 99 | singleton.start(); 100 | } 101 | return singleton; 102 | } 103 | 104 | /** 105 | * Start the thread for file writing 106 | */ 107 | void start() 108 | { 109 | isTerminate = false; 110 | t.start(); 111 | } 112 | 113 | /** 114 | * Terminate the thread for file writing 115 | */ 116 | void stop() 117 | { 118 | isTerminate = true; 119 | } 120 | 121 | /** 122 | * Write data to a particular file indicated by path 123 | */ 124 | void write(String path, SerializedLog data) 125 | { 126 | try { 127 | queue.add(new AbstractMap.SimpleEntry<>(path, data)); 128 | } 129 | catch (IllegalStateException e) { 130 | log.error("Error adding error log to queue. Queue full while capacity is " + QUEUE_CAPACITY + ". Error: " + e.getMessage()); 131 | log.error("Dropped queryID: " + data.getQueryId()); 132 | } 133 | catch (Exception e) { 134 | log.error("Unknown error adding error log to queue. ErrorMessage: " + e.getMessage()); 135 | log.error("Dropped queryID: " + data.getQueryId()); 136 | } 137 | } 138 | 139 | @Override 140 | public void run() 141 | { 142 | while (!isTerminate) { 143 | Map.Entry record; 144 | 145 | // Poll record 146 | try { 147 | // + 1 second before cleanUP to ensure files are marked timeout 148 | record = queue.poll(FILE_TIMEOUT_SEC + 1, TimeUnit.SECONDS); 149 | } 150 | catch (InterruptedException e) { 151 | log.error("Unknown interruptedException." + e); 152 | continue; 153 | } 154 | 155 | if (record == null) { 156 | // Timeout from poll() -> release file handlers 157 | fileWriters.cleanUp(); 158 | } 159 | else { 160 | try { 161 | // New record for writing 162 | FileWriter fileWriter = fileWriters.get(record.getKey()); 163 | fileWriter.write(record.getValue().getSerializedLog()); 164 | fileWriter.write(System.lineSeparator()); 165 | } 166 | catch (Exception e) { 167 | log.error("Error writing event log to file in run()." + e); 168 | log.error("Dropped queryID: " + record.getValue().getQueryId()); 169 | } 170 | } 171 | } 172 | } 173 | 174 | static class WriterFactory 175 | { 176 | FileWriter getFileWriter(String filename) 177 | throws IOException 178 | { 179 | return new FileWriter(filename, true); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/serializer/AuditRecord.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit.serializer; 15 | 16 | public class AuditRecord 17 | { 18 | private String eventType; 19 | 20 | private String queryId; 21 | private String query; 22 | private String uri; 23 | private String state; 24 | 25 | private Double cpuTime; 26 | private Double wallTime; 27 | private Double queuedTime; 28 | private Long peakUserMemoryBytes; 29 | private Long peakTotalNonRevocableMemoryBytes; 30 | private Long totalBytes; 31 | private Long totalRows; 32 | private int completedSplits; 33 | 34 | private Double createTimestamp; 35 | private Double executionStartTimestamp; 36 | private Double endTimestamp; 37 | 38 | private int errorCode; 39 | private String errorName; 40 | private String failureType; 41 | private String failureMessage; 42 | private String failuresJson; 43 | 44 | private String remoteClientAddress; 45 | private String clientUser; 46 | private String userAgent; 47 | private String source; 48 | 49 | public String getEventType() 50 | { 51 | return eventType; 52 | } 53 | 54 | public void setEventType(String eventType) 55 | { 56 | this.eventType = eventType; 57 | } 58 | 59 | public String getQueryId() 60 | { 61 | return queryId; 62 | } 63 | 64 | public void setQueryId(String queryId) 65 | { 66 | this.queryId = queryId; 67 | } 68 | 69 | public String getQuery() 70 | { 71 | return query; 72 | } 73 | 74 | public void setQuery(String query) 75 | { 76 | this.query = query; 77 | } 78 | 79 | public String getUri() 80 | { 81 | return uri; 82 | } 83 | 84 | public void setUri(String uri) 85 | { 86 | this.uri = uri; 87 | } 88 | 89 | public String getState() 90 | { 91 | return state; 92 | } 93 | 94 | public void setState(String state) 95 | { 96 | this.state = state; 97 | } 98 | 99 | public Double getCpuTime() 100 | { 101 | return cpuTime; 102 | } 103 | 104 | public void setCpuTime(Double cpuTime) 105 | { 106 | this.cpuTime = cpuTime; 107 | } 108 | 109 | public Double getWallTime() 110 | { 111 | return wallTime; 112 | } 113 | 114 | public void setWallTime(Double wallTime) 115 | { 116 | this.wallTime = wallTime; 117 | } 118 | 119 | public Double getQueuedTime() 120 | { 121 | return queuedTime; 122 | } 123 | 124 | public void setQueuedTime(Double queuedTime) 125 | { 126 | this.queuedTime = queuedTime; 127 | } 128 | 129 | public Long getTotalBytes() 130 | { 131 | return totalBytes; 132 | } 133 | 134 | public void setTotalBytes(Long totalBytes) 135 | { 136 | this.totalBytes = totalBytes; 137 | } 138 | 139 | public Long getTotalRows() 140 | { 141 | return totalRows; 142 | } 143 | 144 | public void setTotalRows(Long totalRows) 145 | { 146 | this.totalRows = totalRows; 147 | } 148 | 149 | public int getCompletedSplits() 150 | { 151 | return completedSplits; 152 | } 153 | 154 | public void setCompletedSplits(int completedSplits) 155 | { 156 | this.completedSplits = completedSplits; 157 | } 158 | 159 | public int getErrorCode() 160 | { 161 | return errorCode; 162 | } 163 | 164 | public void setErrorCode(int errorCode) 165 | { 166 | this.errorCode = errorCode; 167 | } 168 | 169 | public String getErrorName() 170 | { 171 | return errorName; 172 | } 173 | 174 | public void setErrorName(String errorName) 175 | { 176 | this.errorName = errorName; 177 | } 178 | 179 | public String getFailureType() 180 | { 181 | return failureType; 182 | } 183 | 184 | public void setFailureType(String failureType) 185 | { 186 | this.failureType = failureType; 187 | } 188 | 189 | public String getFailureMessage() 190 | { 191 | return failureMessage; 192 | } 193 | 194 | public void setFailureMessage(String failureMessage) 195 | { 196 | this.failureMessage = failureMessage; 197 | } 198 | 199 | public String getFailuresJson() 200 | { 201 | return failuresJson; 202 | } 203 | 204 | public void setFailuresJson(String failuresJson) 205 | { 206 | this.failuresJson = failuresJson; 207 | } 208 | 209 | public String getRemoteClientAddress() 210 | { 211 | return remoteClientAddress; 212 | } 213 | 214 | public void setRemoteClientAddress(String remoteClientAddress) 215 | { 216 | this.remoteClientAddress = remoteClientAddress; 217 | } 218 | 219 | public String getClientUser() 220 | { 221 | return clientUser; 222 | } 223 | 224 | public void setClientUser(String clientUser) 225 | { 226 | this.clientUser = clientUser; 227 | } 228 | 229 | public String getUserAgent() 230 | { 231 | return userAgent; 232 | } 233 | 234 | public void setUserAgent(String userAgent) 235 | { 236 | this.userAgent = userAgent; 237 | } 238 | 239 | public String getSource() 240 | { 241 | return source; 242 | } 243 | 244 | public void setSource(String source) 245 | { 246 | this.source = source; 247 | } 248 | 249 | public Double getCreateTimestamp() 250 | { 251 | return createTimestamp; 252 | } 253 | 254 | public void setCreateTimestamp(Double createTimestamp) 255 | { 256 | this.createTimestamp = createTimestamp; 257 | } 258 | 259 | public Double getExecutionStartTimestamp() 260 | { 261 | return executionStartTimestamp; 262 | } 263 | 264 | public void setExecutionStartTimestamp(Double executionStartTimestamp) 265 | { 266 | this.executionStartTimestamp = executionStartTimestamp; 267 | } 268 | 269 | public Double getEndTimestamp() 270 | { 271 | return endTimestamp; 272 | } 273 | 274 | public void setEndTimestamp(Double endTimestamp) 275 | { 276 | this.endTimestamp = endTimestamp; 277 | } 278 | 279 | public Long getPeakUserMemoryBytes() 280 | { 281 | return peakUserMemoryBytes; 282 | } 283 | 284 | public void setPeakUserMemoryBytes(Long peakUserMemoryBytes) 285 | { 286 | this.peakUserMemoryBytes = peakUserMemoryBytes; 287 | } 288 | 289 | public Long getPeakTotalNonRevocableMemoryBytes() 290 | { 291 | return peakTotalNonRevocableMemoryBytes; 292 | } 293 | 294 | public void setPeakTotalNonRevocableMemoryBytes(Long peakTotalNonRevocableMemoryBytes) 295 | { 296 | this.peakTotalNonRevocableMemoryBytes = peakTotalNonRevocableMemoryBytes; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/TestAuditLogListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | *

6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | *

8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import org.apache.pulsar.client.api.PulsarClientException; 17 | import org.testng.annotations.Test; 18 | 19 | import java.util.Optional; 20 | 21 | import static org.mockito.ArgumentMatchers.any; 22 | import static org.mockito.ArgumentMatchers.eq; 23 | import static org.mockito.Mockito.mock; 24 | import static org.mockito.Mockito.times; 25 | import static org.mockito.Mockito.verify; 26 | 27 | @Test(singleThreaded = true) 28 | public class TestAuditLogListener 29 | { 30 | TestHelper testHelper = new TestHelper(); 31 | 32 | private AuditLogListener createSimpleAuditLogListener(AuditLogFileWriter auditLogFileWriter) 33 | throws PulsarClientException 34 | { 35 | AuditConfig config = new AuditConfig() 36 | .setAuditLogFileWriter(auditLogFileWriter) 37 | .setAuditLogPath("/test/path") 38 | .setAuditSimpleLogName("test-filename.log"); 39 | return new AuditLogListener(config); 40 | } 41 | 42 | private AuditLogListener createFullAuditLogListener(AuditLogFileWriter auditLogFileWriter) 43 | throws PulsarClientException 44 | { 45 | AuditConfig config = new AuditConfig() 46 | .setAuditLogFileWriter(auditLogFileWriter) 47 | .setAuditLogPath("/test/path_full") 48 | .setAuditSimpleLogName("test-filename.log") 49 | .setAuditFullLogName("test-filename-full.log"); 50 | return new AuditLogListener(config); 51 | } 52 | 53 | private AuditLogListener createFullAuditLogListenerWithFilter(AuditLogFileWriter auditLogFileWriter, String filter) 54 | throws PulsarClientException 55 | { 56 | AuditConfig config = new AuditConfig() 57 | .setAuditLogFileWriter(auditLogFileWriter) 58 | .setAuditLogPath("/test/path_full") 59 | .setAuditSimpleLogName("test-filename.log") 60 | .setAuditFullLogName("test-filename-full.log") 61 | .setLogFilter(filter); 62 | return new AuditLogListener(config); 63 | } 64 | 65 | @Test(expectedExceptions = NullPointerException.class) 66 | public void testCreateAuditLogListenerEmpty() 67 | throws PulsarClientException 68 | { 69 | AuditConfig config = new AuditConfig(); 70 | new AuditLogListener(config); 71 | } 72 | 73 | @Test(expectedExceptions = NullPointerException.class) 74 | public void testCreateAuditLogListenerMissingPath() 75 | throws PulsarClientException 76 | { 77 | AuditConfig config = new AuditConfig() 78 | .setAuditSimpleLogName("test-filename.log"); 79 | new AuditLogListener(config); 80 | } 81 | 82 | @Test(expectedExceptions = NullPointerException.class) 83 | public void testCreateAuditLogListenerMissingFilename() 84 | throws PulsarClientException 85 | { 86 | AuditConfig config = new AuditConfig() 87 | .setAuditLogPath("/test/path"); 88 | new AuditLogListener(config); 89 | } 90 | 91 | @Test 92 | public void testCreateAuditLogListenerSuccessWithoutFullFilename() 93 | throws PulsarClientException 94 | { 95 | createSimpleAuditLogListener(AuditLogFileWriter.getInstance()); 96 | } 97 | 98 | @Test 99 | public void testCreateAuditLogListenerSuccessWithFullFilename() 100 | throws PulsarClientException 101 | { 102 | createFullAuditLogListener(AuditLogFileWriter.getInstance()); 103 | } 104 | 105 | @Test 106 | public void testSimpleLogQueryCompleted() 107 | throws PulsarClientException 108 | { 109 | AuditLogFileWriter auditLogFileWriterMock = mock(AuditLogFileWriter.class); 110 | AuditLogListener auditLogListener = createSimpleAuditLogListener(auditLogFileWriterMock); 111 | 112 | auditLogListener.queryCompleted(testHelper.createNormalEvent()); 113 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path/test-filename.log"), any()); 114 | } 115 | 116 | @Test 117 | public void testFullLogQueryCompleted() 118 | throws PulsarClientException 119 | { 120 | AuditLogFileWriter auditLogFileWriterMock = mock(AuditLogFileWriter.class); 121 | AuditLogListener auditLogListener = createFullAuditLogListener(auditLogFileWriterMock); 122 | 123 | auditLogListener.queryCompleted(testHelper.createNormalEvent()); 124 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename.log"), any()); 125 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename-full.log"), any()); 126 | } 127 | 128 | @Test 129 | public void testFilter() 130 | throws PulsarClientException 131 | { 132 | AuditLogFileWriter auditLogFileWriterMock = mock(AuditLogFileWriter.class); 133 | AuditLogListener auditLogListener = createFullAuditLogListenerWithFilter(auditLogFileWriterMock, "SRE_SYSTEM"); 134 | 135 | auditLogListener.queryCompleted(testHelper.createNormalEvent()); 136 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename.log"), any()); 137 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename-full.log"), any()); 138 | 139 | auditLogListener.queryCompleted(testHelper.createQueryWithSource(Optional.of("SRE_SYSTEM"))); 140 | verify(auditLogFileWriterMock, times(2)).write(eq("/test/path_full/test-filename.log"), any()); 141 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename-full.log"), any()); 142 | } 143 | 144 | @Test 145 | public void testFilterComplex() 146 | throws PulsarClientException 147 | { 148 | AuditLogFileWriter auditLogFileWriterMock = mock(AuditLogFileWriter.class); 149 | AuditLogListener auditLogListener = createFullAuditLogListenerWithFilter(auditLogFileWriterMock, "(SRE_SYSTEM|Presto_team)"); 150 | 151 | auditLogListener.queryCompleted(testHelper.createNormalEvent()); 152 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename.log"), any()); 153 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename-full.log"), any()); 154 | 155 | auditLogListener.queryCompleted(testHelper.createQueryWithSource(Optional.of("SRE_SYSTEM"))); 156 | verify(auditLogFileWriterMock, times(2)).write(eq("/test/path_full/test-filename.log"), any()); 157 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename-full.log"), any()); 158 | 159 | auditLogListener.queryCompleted(testHelper.createQueryWithSource(Optional.of("Presto_team"))); 160 | verify(auditLogFileWriterMock, times(3)).write(eq("/test/path_full/test-filename.log"), any()); 161 | verify(auditLogFileWriterMock, times(1)).write(eq("/test/path_full/test-filename-full.log"), any()); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/checkstyle/checks.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 143 | 144 | 145 | 146 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/TestHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import com.facebook.presto.spi.ErrorCode; 17 | import com.facebook.presto.spi.ErrorType; 18 | import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; 19 | import com.facebook.presto.spi.eventlistener.QueryContext; 20 | import com.facebook.presto.spi.eventlistener.QueryFailureInfo; 21 | import com.facebook.presto.spi.eventlistener.QueryIOMetadata; 22 | import com.facebook.presto.spi.eventlistener.QueryInputMetadata; 23 | import com.facebook.presto.spi.eventlistener.QueryMetadata; 24 | import com.facebook.presto.spi.eventlistener.QueryStatistics; 25 | import com.facebook.presto.spi.eventlistener.StageCpuDistribution; 26 | import com.facebook.presto.spi.resourceGroups.ResourceGroupId; 27 | import com.facebook.presto.spi.session.ResourceEstimates; 28 | import io.airlift.units.DataSize; 29 | 30 | import java.net.URI; 31 | import java.time.Duration; 32 | import java.time.Instant; 33 | import java.time.ZoneId; 34 | import java.time.ZonedDateTime; 35 | import java.util.ArrayList; 36 | import java.util.Collections; 37 | import java.util.HashMap; 38 | import java.util.HashSet; 39 | import java.util.Optional; 40 | 41 | public class TestHelper 42 | { 43 | private URI uri; 44 | private QueryStatistics statistics; 45 | private QueryContext context; 46 | private QueryIOMetadata ioMetadata; 47 | private Instant createTime; 48 | private Instant executionStartTime; 49 | private Instant endTime; 50 | 51 | public void setUp() 52 | { 53 | try { 54 | uri = new URI("http://example.com:8080/v1/query/20170521_140224_00002_gd5k3"); 55 | statistics = new QueryStatistics(Duration.ofMillis(100), 56 | Duration.ofMillis(200), 57 | Duration.ofMillis(300), 58 | Optional.of(Duration.ofMillis(400)), 59 | Optional.of(Duration.ofMillis(500)), 60 | 10001, 61 | 10002, 62 | 10003, 63 | 0, 64 | 14, 65 | 0, 66 | 0, 67 | 0, 68 | 0, 69 | 2048.0, 70 | new ArrayList<>(), 71 | 4096, 72 | true, 73 | new ArrayList() {}, 74 | new ArrayList() 75 | {{ 76 | add("operatorSummaries 01"); 77 | }} 78 | ); 79 | context = new QueryContext( 80 | "test-user", 81 | Optional.of("principal"), 82 | Optional.of("example.com"), 83 | Optional.of("StatementClient 0.167"), 84 | Optional.of("clientInfo"), 85 | new HashSet<>(), 86 | new HashSet<>(), 87 | Optional.of("presto-cli"), 88 | Optional.of("catalog"), 89 | Optional.of("schema"), 90 | Optional.of(new ResourceGroupId("testResourceGroup")), 91 | new HashMap<>(), 92 | new ResourceEstimates( 93 | Optional.of(io.airlift.units.Duration.valueOf("1s")), 94 | Optional.of(io.airlift.units.Duration.valueOf("1s")), 95 | Optional.of(DataSize.valueOf("6TB"))), 96 | "127.0.0.1", 97 | "0.175", 98 | "environment"); 99 | ioMetadata = new QueryIOMetadata(new ArrayList(), 100 | Optional.empty()); 101 | ZoneId jst_zone = ZoneId.of("Asia/Tokyo"); 102 | createTime = ZonedDateTime.of(2017, 6 + 1, 15, 10, 0, 0, 0, jst_zone).toInstant(); 103 | executionStartTime = ZonedDateTime.of(2017, 6 + 1, 15, 10, 0, 1, 0, jst_zone).toInstant(); 104 | endTime = ZonedDateTime.of(2017, 6 + 1, 15, 10, 0, 3, 0, jst_zone).toInstant(); 105 | } 106 | catch (Exception ignoreException) { 107 | ignoreException.printStackTrace(); 108 | 109 | } 110 | } 111 | 112 | public QueryCompletedEvent createNormalEvent() 113 | { 114 | setUp(); 115 | QueryMetadata metadata = new QueryMetadata("20170606_044544_00024_nfhe3", 116 | Optional.of("4c52973c-14c6-4534-837f-238e21d9b061"), 117 | "select * from airdelays_s3_csv WHERE kw = 'presto-kw-example' limit 5", 118 | "FINISHED", 119 | uri, 120 | Optional.empty(), 121 | Optional.empty()); 122 | return new QueryCompletedEvent(metadata, 123 | statistics, 124 | context, 125 | ioMetadata, 126 | Optional.empty(), 127 | Collections.emptyList(), 128 | createTime, 129 | executionStartTime, 130 | endTime); 131 | } 132 | 133 | public QueryCompletedEvent createFailureEvent() 134 | { 135 | setUp(); 136 | QueryMetadata metadata = new QueryMetadata("20170606_044544_00024_nfhe3", 137 | Optional.of("4c52973c-14c6-4534-837f-238e21d9b061"), 138 | "select 2a", 139 | "FAILED", 140 | uri, 141 | Optional.empty(), 142 | Optional.empty()); 143 | ErrorCode errorCode = new ErrorCode(1, "SYNTAX_ERROR", ErrorType.USER_ERROR); 144 | QueryFailureInfo failureInfo = new QueryFailureInfo(errorCode, 145 | Optional.of("com.facebook.presto.sql.parser.ParsingException"), 146 | Optional.of("line 1:15: mismatched input '0' expecting ')'"), 147 | Optional.empty(), 148 | Optional.empty(), 149 | "{json-error}" 150 | ); 151 | 152 | return new QueryCompletedEvent(metadata, 153 | statistics, 154 | context, 155 | ioMetadata, 156 | Optional.of(failureInfo), 157 | Collections.emptyList(), 158 | createTime, 159 | executionStartTime, 160 | endTime); 161 | } 162 | 163 | public QueryCompletedEvent createQueryWithSource(Optional source) 164 | { 165 | setUp(); 166 | QueryMetadata metadata = new QueryMetadata("20170606_044544_00024_nfhe3", 167 | Optional.of("4c52973c-14c6-4534-837f-238e21d9b061"), 168 | "select * from airdelays_s3_csv WHERE kw = 'presto-kw-example' limit 5", 169 | "FINISHED", 170 | uri, 171 | Optional.empty(), 172 | Optional.empty()); 173 | QueryContext context = new QueryContext( 174 | "test-user", 175 | Optional.of("principal"), 176 | Optional.of("example.com"), 177 | Optional.of("StatementClient 0.167"), 178 | Optional.of("clientInfo"), 179 | new HashSet<>(), 180 | new HashSet<>(), 181 | source, 182 | Optional.of("catalog"), 183 | Optional.of("schema"), 184 | Optional.of(new ResourceGroupId("testResourceGroup")), 185 | new HashMap<>(), 186 | new ResourceEstimates( 187 | Optional.of(io.airlift.units.Duration.valueOf("1s")), 188 | Optional.of(io.airlift.units.Duration.valueOf("1s")), 189 | Optional.of(DataSize.valueOf("6TB"))), 190 | "127.0.0.1", 191 | "0.175", 192 | "environment"); 193 | return new QueryCompletedEvent(metadata, 194 | statistics, 195 | context, 196 | ioMetadata, 197 | Optional.empty(), 198 | Collections.emptyList(), 199 | createTime, 200 | executionStartTime, 201 | endTime); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/jp/co/yahoo/presto/audit/AuditConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | *

6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | *

8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import io.airlift.configuration.Config; 17 | import io.airlift.configuration.ConfigDescription; 18 | import org.apache.pulsar.client.api.CompressionType; 19 | 20 | import javax.annotation.Nullable; 21 | import javax.validation.constraints.NotNull; 22 | 23 | public class AuditConfig 24 | { 25 | private String auditLogPath; 26 | private String auditSimpleLogName; 27 | private String auditFullLogName; 28 | private String simpleLogTopic; 29 | private String fullLogTopic; 30 | private String url; 31 | private String trustCerts; 32 | private String logFilter; 33 | private String tenantDomain; 34 | private String tenantService; 35 | private String providerDomain; 36 | private String privateKey; 37 | private String athenzConfPath; 38 | private String principalHeader; 39 | private String roleHeader; 40 | private CompressionType compressionType = CompressionType.NONE; 41 | private AuditLogFileWriter auditLogFileWriter = AuditLogFileWriter.getInstance(); 42 | private boolean useTLS = true; 43 | 44 | @NotNull 45 | public AuditLogFileWriter getAuditLogFileWriter() 46 | { 47 | return auditLogFileWriter; 48 | } 49 | 50 | public AuditConfig setAuditLogFileWriter(AuditLogFileWriter auditLogFileWriter) 51 | { 52 | this.auditLogFileWriter = auditLogFileWriter; 53 | return this; 54 | } 55 | 56 | @NotNull 57 | public String getAuditLogPath() 58 | { 59 | return auditLogPath; 60 | } 61 | 62 | @Config("event-listener.audit-log-path") 63 | @ConfigDescription("audit log path") 64 | public AuditConfig setAuditLogPath(String eventLogPath) 65 | { 66 | this.auditLogPath = eventLogPath; 67 | return this; 68 | } 69 | 70 | @NotNull 71 | public String getAuditSimpleLogName() 72 | { 73 | return auditSimpleLogName; 74 | } 75 | 76 | @Config("event-listener.audit-log-filename") 77 | @ConfigDescription("audit simple log file name") 78 | public AuditConfig setAuditSimpleLogName(String auditSimpleLogName) 79 | { 80 | this.auditSimpleLogName = auditSimpleLogName; 81 | return this; 82 | } 83 | 84 | @Nullable 85 | public String getAuditFullLogName() 86 | { 87 | return auditFullLogName; 88 | } 89 | 90 | @Config("event-listener.audit-log-full-filename") 91 | @ConfigDescription("audit full log file name") 92 | public AuditConfig setAuditFullLogName(String auditFullLogName) 93 | { 94 | this.auditFullLogName = auditFullLogName; 95 | return this; 96 | } 97 | 98 | @Nullable 99 | public String getSimpleTopic() 100 | { 101 | return simpleLogTopic; 102 | } 103 | 104 | @Config("event-listener.pulsar.simple-log-topic") 105 | @ConfigDescription("simple log topic") 106 | public AuditConfig setSimpleTopic(String simpleLogTopic) 107 | { 108 | this.simpleLogTopic = simpleLogTopic; 109 | return this; 110 | } 111 | 112 | @Nullable 113 | public String getFullTopic() 114 | { 115 | return fullLogTopic; 116 | } 117 | 118 | @Config("event-listener.pulsar.full-log-topic") 119 | @ConfigDescription("full log topic") 120 | public AuditConfig setFullTopic(String fullLogTopic) 121 | { 122 | this.fullLogTopic = fullLogTopic; 123 | return this; 124 | } 125 | 126 | @Nullable 127 | public String getPrivateKeyPath() 128 | { 129 | return privateKey; 130 | } 131 | 132 | @Config("event-listener.athenz.private-key-path") 133 | @ConfigDescription("private key path") 134 | public AuditConfig setPrivateKeyPath(String privateKey) 135 | { 136 | this.privateKey = privateKey; 137 | return this; 138 | } 139 | 140 | @Nullable 141 | public String getPulsarUrl() 142 | { 143 | return url; 144 | } 145 | 146 | @Config("event-listener.pulsar.pulsar-url") 147 | @ConfigDescription("pulsar url") 148 | public AuditConfig setPulsarUrl(String pulsarUrl) 149 | { 150 | this.url = pulsarUrl; 151 | return this; 152 | } 153 | 154 | @Nullable 155 | public String getTrustCertsPath() 156 | { 157 | return trustCerts; 158 | } 159 | 160 | @Config("event-listener.pulsar.pulsar-cert-path") 161 | @ConfigDescription("pulsar cert path") 162 | public AuditConfig setTrustCertsPath(String trustCertsPath) 163 | { 164 | this.trustCerts = trustCertsPath; 165 | return this; 166 | } 167 | 168 | @NotNull 169 | public boolean getUseTLS() 170 | { 171 | return useTLS; 172 | } 173 | 174 | @Config("event-listener.pulsar.use-TLS") 175 | @ConfigDescription("audit log path") 176 | public AuditConfig setUseTLS(boolean useTLS) 177 | { 178 | this.useTLS = useTLS; 179 | return this; 180 | } 181 | 182 | @NotNull 183 | public CompressionType getCompressionType() 184 | { 185 | return compressionType; 186 | } 187 | 188 | @Config("event-listener.pulsar.compression-type") 189 | @ConfigDescription("message compression type (NONE, LZ4 or ZLIB)") 190 | public AuditConfig setCompressionType(CompressionType compressionType) 191 | { 192 | this.compressionType = compressionType; 193 | return this; 194 | } 195 | 196 | @Nullable 197 | public String getLogFilter() 198 | { 199 | return logFilter; 200 | } 201 | 202 | @Config("event-listener.audit-log-full-filter") 203 | @ConfigDescription("audit log full filter") 204 | public AuditConfig setLogFilter(String auditLogFullFilter) 205 | { 206 | this.logFilter = auditLogFullFilter; 207 | return this; 208 | } 209 | 210 | @Nullable 211 | public String getTenantDomain() 212 | { 213 | return tenantDomain; 214 | } 215 | 216 | @Config("event-listener.athenz.tenant-domain") 217 | @ConfigDescription("tenant domain name") 218 | public AuditConfig setTenantDomain(String athenzTenantDomain) 219 | { 220 | this.tenantDomain = athenzTenantDomain; 221 | return this; 222 | } 223 | 224 | @Nullable 225 | public String getTenantService() 226 | { 227 | return tenantService; 228 | } 229 | 230 | @Config("event-listener.athenz.tenant-service") 231 | @ConfigDescription("tenant service name") 232 | public AuditConfig setTenantService(String athenzTenantService) 233 | { 234 | this.tenantService = athenzTenantService; 235 | return this; 236 | } 237 | 238 | @Nullable 239 | public String getProviderDomain() 240 | { 241 | return providerDomain; 242 | } 243 | 244 | @Config("event-listener.athenz.provider-domain") 245 | @ConfigDescription("athenz provider domain") 246 | public AuditConfig setProviderDomain(String athenzProviderDomain) 247 | { 248 | this.providerDomain = athenzProviderDomain; 249 | return this; 250 | } 251 | 252 | @Nullable 253 | public String getAthenzConfPath() 254 | { 255 | return athenzConfPath; 256 | } 257 | 258 | @Config("event-listener.athenz.config-path") 259 | @ConfigDescription("athenz config path") 260 | public AuditConfig setAthenzConfPath(String athenzConfigPath) 261 | { 262 | this.athenzConfPath = athenzConfigPath; 263 | return this; 264 | } 265 | 266 | @Nullable 267 | public String getPrincipalHeader() 268 | { 269 | return principalHeader; 270 | } 271 | 272 | @Config("event-listener.athenz.principal-header") 273 | @ConfigDescription("athenz principal header") 274 | public AuditConfig setPrincipalHeader(String athenzPrincipalHeader) 275 | { 276 | this.principalHeader = athenzPrincipalHeader; 277 | return this; 278 | } 279 | 280 | @Nullable 281 | public String getRoleHeader() 282 | { 283 | return roleHeader; 284 | } 285 | 286 | @Config("event-listener.athenz.role-header") 287 | @ConfigDescription("athenz role header") 288 | public AuditConfig setRoleHeader(String athenzRoleHeader) 289 | { 290 | this.roleHeader = athenzRoleHeader; 291 | return this; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | jp.co.yahoo.presto.plugin 6 | presto-audit-plugin 7 | Presto - Audit log 8 | 2.2-SNAPSHOT 9 | jar 10 | 11 | 12 | 13 | Apache License 2.0 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | repo 16 | 17 | 18 | 19 | 20 | scm:git:git://github.com/yahoojapan/presto-audit.git 21 | https://github.com/yahoojapan/presto-audit 22 | HEAD 23 | 24 | 25 | 26 | 0.176 27 | 0.214 28 | UTF-8 29 | 1.8 30 | 1.8 31 | 0 32 | 33 | 34 | 35 | 36 | io.airlift 37 | log 38 | ${dep.airlift.version} 39 | 40 | 41 | io.airlift 42 | json 43 | ${dep.airlift.version} 44 | 45 | 46 | io.airlift 47 | configuration 48 | ${dep.airlift.version} 49 | 50 | 51 | io.airlift 52 | bootstrap 53 | ${dep.airlift.version} 54 | 55 | 56 | 57 | com.google.guava 58 | guava 59 | 22.0 60 | 61 | 62 | 63 | 64 | com.facebook.presto 65 | presto-spi 66 | ${presto.version} 67 | provided 68 | 69 | 70 | 71 | io.airlift 72 | units 73 | 1.0 74 | provided 75 | 76 | 77 | 78 | com.fasterxml.jackson.core 79 | jackson-annotations 80 | 2.9.7 81 | provided 82 | 83 | 84 | 85 | 86 | com.google.code.gson 87 | gson 88 | 2.2.4 89 | 90 | 91 | 92 | 93 | com.facebook.presto 94 | presto-main 95 | ${presto.version} 96 | test 97 | 98 | 99 | 100 | org.assertj 101 | assertj-core 102 | 3.0.0 103 | test 104 | 105 | 106 | 107 | org.testng 108 | testng 109 | 6.10 110 | test 111 | 112 | 113 | 114 | io.airlift 115 | testing 116 | ${dep.airlift.version} 117 | test 118 | 119 | 120 | 121 | io.airlift 122 | http-server 123 | ${dep.airlift.version} 124 | test 125 | 126 | 127 | 128 | org.mockito 129 | mockito-core 130 | 2.16.0 131 | test 132 | 133 | 134 | 135 | io.airlift 136 | node 137 | ${dep.airlift.version} 138 | test 139 | 140 | 141 | 142 | org.apache.pulsar 143 | pulsar-client 144 | 1.22.1-incubating 145 | 146 | 147 | 148 | org.apache.pulsar 149 | pulsar-client-auth-athenz 150 | 1.22.1-incubating 151 | 152 | 153 | 154 | 155 | src/main/java 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-checkstyle-plugin 160 | 2.17 161 | 162 | 163 | validate 164 | validate 165 | 166 | src/checkstyle/checks.xml 167 | UTF-8 168 | true 169 | true 170 | 171 | 172 | check 173 | 174 | 175 | 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-compiler-plugin 181 | 3.1 182 | 183 | ${maven.compiler.source} 184 | ${maven.compiler.target} 185 | UTF-8 186 | true 187 | 188 | 189 | 190 | 191 | io.takari.maven.plugins 192 | presto-maven-plugin 193 | 0.1.12 194 | true 195 | 196 | 197 | 198 | 199 | org.apache.maven.plugins 200 | maven-shade-plugin 201 | 3.1.0 202 | 203 | 204 | package 205 | 206 | shade 207 | 208 | 209 | true 210 | all 211 | 212 | 213 | *:* 214 | 215 | META-INF/*.SF 216 | META-INF/*.DSA 217 | META-INF/*.RSA 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | com.mycila 228 | license-maven-plugin 229 | 3.0 230 | 231 |

${project.basedir}/src/license/LICENSE-HEADER.txt
232 | 233 | *.xml 234 | src/checkstyle/* 235 | src/sql/* 236 | .mvn/** 237 | mvnw* 238 | 239 | 240 | 241 | 242 | 243 | org.apache.maven.plugins 244 | maven-release-plugin 245 | 2.5.3 246 | 247 | forked-path 248 | false 249 | true 250 | false 251 | clean install 252 | true 253 | @{project.version} 254 | 255 | 256 | 257 | 258 | org.apache.maven.plugins 259 | maven-surefire-plugin 260 | 2.20.1 261 | 262 | 263 | Asia/Tokyo 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | org.codehaus.mojo 273 | rpm-maven-plugin 274 | 2.1.5 275 | 276 | 286 | 287 | 288 | presto-audit-plugin 289 | ${project.version} 290 | Applications/Databases 291 | Presto audit plugin Package. 292 | ASL 2.0 293 | ${build.no}.el7 294 | true 295 | 0 296 | https://github.com/yahoojapan/presto-audit 297 | 298 | 299 | /usr/lib/presto/lib/plugin/yj-audit 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | -------------------------------------------------------------------------------- /src/test/java/jp/co/yahoo/presto/audit/TestAuditLogFileWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package jp.co.yahoo.presto.audit; 15 | 16 | import io.airlift.log.Logger; 17 | import jp.co.yahoo.presto.audit.serializer.SerializedLog; 18 | import org.mockito.ArgumentCaptor; 19 | import org.mockito.stubbing.Answer; 20 | import org.testng.annotations.Test; 21 | 22 | import java.io.FileWriter; 23 | import java.io.IOException; 24 | import java.lang.reflect.Constructor; 25 | import java.util.List; 26 | import java.util.stream.Collectors; 27 | 28 | import static jp.co.yahoo.presto.audit.AuditLogFileWriter.WriterFactory; 29 | import static org.mockito.ArgumentMatchers.any; 30 | import static org.mockito.ArgumentMatchers.anyString; 31 | import static org.mockito.ArgumentMatchers.eq; 32 | import static org.mockito.Mockito.atLeastOnce; 33 | import static org.mockito.Mockito.doAnswer; 34 | import static org.mockito.Mockito.doThrow; 35 | import static org.mockito.Mockito.mock; 36 | import static org.mockito.Mockito.spy; 37 | import static org.mockito.Mockito.times; 38 | import static org.mockito.Mockito.verify; 39 | import static org.mockito.Mockito.when; 40 | 41 | @Test(singleThreaded = true, threadPoolSize = 1) 42 | public class TestAuditLogFileWriter 43 | { 44 | private static final String QUERY_ID = "20170606_044544_00024_abcde"; 45 | private void pause() 46 | { 47 | try { 48 | Thread.sleep(6000); 49 | } 50 | catch (Exception e) { 51 | } 52 | } 53 | 54 | private void initTest() 55 | { 56 | pause(); 57 | StackTraceElement[] stackTrace = Thread.currentThread() 58 | .getStackTrace(); 59 | System.out.println("==========================="); 60 | System.out.println(stackTrace[2].getMethodName()); 61 | System.out.flush(); 62 | } 63 | 64 | private AuditLogFileWriter getNewAuditLogFileWriter(WriterFactory writerFactory, Logger logger) throws Exception 65 | { 66 | Constructor constructor = AuditLogFileWriter.class.getDeclaredConstructor(WriterFactory.class, Logger.class); 67 | constructor.setAccessible(true); 68 | AuditLogFileWriter auditLogFileWriter = constructor.newInstance(writerFactory, logger); 69 | auditLogFileWriter.start(); 70 | return auditLogFileWriter; 71 | } 72 | 73 | @Test 74 | public void testSingleton() 75 | { 76 | AuditLogFileWriter auditLogFileWriter_1 = AuditLogFileWriter.getInstance(); 77 | AuditLogFileWriter auditLogFileWriter_2 = AuditLogFileWriter.getInstance(); 78 | assert(auditLogFileWriter_1==auditLogFileWriter_2); 79 | } 80 | 81 | @Test 82 | public void testNormalWrite() 83 | { 84 | initTest(); 85 | final String FILE_NAME = "/tmp/file1"; 86 | final String DATA = "{\"data\":\"value\"}"; 87 | 88 | AuditLogFileWriter auditLogFileWriter = AuditLogFileWriter.getInstance(); 89 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 90 | } 91 | 92 | @Test 93 | public void testWriteAutoCloseFile() throws Exception 94 | { 95 | initTest(); 96 | final String FILE_NAME = "/tmp/file1"; 97 | final String DATA = "{\"data\":\"value\"}"; 98 | 99 | // Setup Spy FileWriter 100 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 101 | final FileWriter[] spyFileWriter = new FileWriter[10]; 102 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 103 | String filename = i.getArgument(0); 104 | assert(filename.equals(FILE_NAME)); 105 | FileWriter fileWriter = new FileWriter(filename, true); 106 | spyFileWriter[0] = spy(fileWriter); 107 | doAnswer((Answer) var1 -> { 108 | System.out.println("WRITING: " + filename + " -- " + var1.getArgument(0).toString().replaceAll("\n", "\\\\n")); 109 | return ""; 110 | }).when(spyFileWriter[0]).write(anyString()); 111 | return spyFileWriter[0]; 112 | }); 113 | 114 | // Test write 115 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, Logger.get("testWriteAutoCloseFile")); 116 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 117 | 118 | // Verify 119 | pause(); 120 | verify(spyFileWriter[0]).write(DATA); 121 | verify(spyFileWriter[0]).close(); 122 | } 123 | 124 | @Test 125 | public void testWriteCloseFileException() throws Exception 126 | { 127 | initTest(); 128 | final String FILE_NAME = "/tmp/file1"; 129 | final String DATA = "{\"data\":\"value\"}"; 130 | 131 | // Setup Spy FileWriter 132 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 133 | final FileWriter[] spyFileWriter = new FileWriter[10]; 134 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 135 | String filename = i.getArgument(0); 136 | assert(filename.equals(FILE_NAME)); 137 | FileWriter fileWriter = new FileWriter(filename, true); 138 | spyFileWriter[0] = spy(fileWriter); 139 | doAnswer((Answer) var1 -> { 140 | System.out.println("WRITING: " + filename + " -- " + var1.getArgument(0).toString().replaceAll("\n", "\\\\n")); 141 | return ""; 142 | }).when(spyFileWriter[0]).write(anyString()); 143 | doThrow(new IOException("Mock close file exception")).when(spyFileWriter[0]).close(); 144 | return spyFileWriter[0]; 145 | }); 146 | 147 | // Test write 148 | Logger logger = spy(Logger.get("testWriteCloseFileException")); 149 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, logger); 150 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 151 | 152 | // Verify writer 153 | pause(); 154 | verify(spyFileWriter[0]).write(DATA); 155 | 156 | // Verify logger 157 | verify(logger, atLeastOnce()).error(anyString()); 158 | } 159 | 160 | @Test 161 | public void testOpenFileException() throws Exception 162 | { 163 | // initTest(); 164 | final String FILE_NAME = "/tmp/file1"; 165 | final String DATA = "{\"data\":\"value\"}"; 166 | 167 | // Setup Spy FileWriter 168 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 169 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 170 | throw new IOException("Mock open file error exception"); 171 | }); 172 | 173 | // Test write 174 | Logger logger = spy(Logger.get("testOpenFileException")); 175 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, logger); 176 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 177 | 178 | pause(); 179 | ArgumentCaptor argument = ArgumentCaptor.forClass(String.class); 180 | verify(logger, atLeastOnce()).error(argument.capture()); 181 | List values = argument.getAllValues(); 182 | assert(values.stream().filter(k -> k.contains(QUERY_ID)) 183 | .collect(Collectors.toList()).size() > 0); 184 | } 185 | 186 | @Test 187 | public void testWriteException() throws Exception 188 | { 189 | initTest(); 190 | final String FILE_NAME = "/tmp/file1"; 191 | final String DATA = "{\"data\":\"value\"}"; 192 | 193 | // Setup Spy FileWriter 194 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 195 | final FileWriter[] spyFileWriter = new FileWriter[10]; 196 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 197 | String filename = i.getArgument(0); 198 | assert(filename.equals(FILE_NAME)); 199 | FileWriter fileWriter = new FileWriter(filename, true); 200 | spyFileWriter[0] = spy(fileWriter); 201 | doThrow(new IOException("Mock write file exception")).when(spyFileWriter[0]).write(anyString()); 202 | return spyFileWriter[0]; 203 | }); 204 | 205 | // Test write 206 | Logger logger = spy(Logger.get("testWriteException")); 207 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, logger); 208 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 209 | 210 | pause(); 211 | ArgumentCaptor argument = ArgumentCaptor.forClass(String.class); 212 | verify(logger, atLeastOnce()).error(argument.capture()); 213 | List values = argument.getAllValues(); 214 | assert(values.stream().filter(k -> k.contains(QUERY_ID)) 215 | .collect(Collectors.toList()).size() > 0); 216 | } 217 | 218 | @Test 219 | public void testFullCapacityWrite() throws Exception 220 | { 221 | initTest(); 222 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 223 | final FileWriter[] spyFileWriter = new FileWriter[10]; 224 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 225 | String filename = i.getArgument(0); 226 | FileWriter fileWriter = new FileWriter(filename, true); 227 | spyFileWriter[0] = spy(fileWriter); 228 | return spyFileWriter[0]; 229 | }); 230 | 231 | // Should log Queue full error 232 | Logger logger = spy(Logger.get("testFullCapacityWrite")); 233 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, logger); 234 | auditLogFileWriter.stop(); 235 | for(int i=0; i<10005; i++) { 236 | auditLogFileWriter.write("/tmp/file1", new SerializedLog(QUERY_ID, "data1")); 237 | } 238 | } 239 | 240 | @Test 241 | public void testMultipleWriteThenClose() throws Exception 242 | { 243 | initTest(); 244 | final String FILE_NAME = "/tmp/file1"; 245 | final String DATA = "{\"data\":\"value\"}"; 246 | 247 | // Setup Spy FileWriter 248 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 249 | final FileWriter[] spyFileWriter = new FileWriter[10]; 250 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 251 | String filename = i.getArgument(0); 252 | assert(filename.equals(FILE_NAME)); 253 | FileWriter fileWriter = new FileWriter(filename, true); 254 | spyFileWriter[0] = spy(fileWriter); 255 | doAnswer((Answer) var1 -> { 256 | System.out.println("WRITING: " + filename + " -- " + var1.getArgument(0).toString().replaceAll("\n", "\\\\n")); 257 | return ""; 258 | }).when(spyFileWriter[0]).write(anyString()); 259 | return spyFileWriter[0]; 260 | }); 261 | 262 | // Test write 263 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, Logger.get("testMultipleWriteThenClose")); 264 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 265 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 266 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 267 | 268 | // Verify 269 | pause(); 270 | verify(spyFileWriter[0], times(3)).write(DATA); 271 | verify(spyFileWriter[0], times(1)).close(); 272 | } 273 | 274 | @Test 275 | public void testTimeoutReopenFile() throws Exception 276 | { 277 | initTest(); 278 | final String FILE_NAME = "/tmp/file1"; 279 | final String DATA = "{\"data\":\"value\"}"; 280 | final String DATA2 = "{\"data2\":\"value\"}"; 281 | 282 | // Setup Spy FileWriter 283 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 284 | final FileWriter[] spyFileWriter = new FileWriter[10]; 285 | when(writerFactoryMock.getFileWriter(any(String .class))).thenAnswer(i -> { 286 | String filename = i.getArgument(0); 287 | assert(filename.equals(FILE_NAME)); 288 | FileWriter fileWriter = new FileWriter(filename, true); 289 | spyFileWriter[0] = spy(fileWriter); 290 | doAnswer((Answer) var1 -> { 291 | System.out.println("WRITING: " + filename + " -- " + var1.getArgument(0).toString().replaceAll("\n", "\\\\n")); 292 | return ""; 293 | }).when(spyFileWriter[0]).write(anyString()); 294 | return spyFileWriter[0]; 295 | }); 296 | 297 | // Test write 298 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, Logger.get("testTimeoutReopenFile")); 299 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 300 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 301 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA)); 302 | 303 | // Verify 304 | pause(); 305 | verify(spyFileWriter[0], times(3)).write(DATA); 306 | verify(spyFileWriter[0], times(1)).close(); 307 | 308 | // Write again after timeout 309 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA2)); 310 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA2)); 311 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA2)); 312 | verify(spyFileWriter[0], times(3)).write(DATA); 313 | verify(spyFileWriter[0], times(1)).close(); 314 | } 315 | 316 | @Test 317 | public void testMultiFileMultiWriteThenClose() throws Exception 318 | { 319 | initTest(); 320 | final String FILE_NAME = "/tmp/file1"; 321 | final String DATA_A1 = "{\"dataA1\":\"value\"}"; 322 | final String DATA_A2 = "{\"dataA2\":\"value\"}"; 323 | 324 | final String FILE_NAME_2 = "/tmp/file2"; 325 | final String DATA_B1 = "{\"data2B1\":\"value2\"}"; 326 | final String DATA_B2 = "{\"data2B2\":\"value2\"}"; 327 | 328 | // Setup Spy FileWriter 329 | WriterFactory writerFactoryMock = mock(WriterFactory.class); 330 | final FileWriter[] spyFileWriter = new FileWriter[10]; 331 | when(writerFactoryMock.getFileWriter(eq(FILE_NAME))).thenAnswer(i -> { 332 | String filename = i.getArgument(0); 333 | assert(filename.equals(FILE_NAME)); 334 | FileWriter fileWriter = new FileWriter(filename, true); 335 | spyFileWriter[0] = spy(fileWriter); 336 | doAnswer((Answer) var1 -> { 337 | System.out.println("WRITING: " + filename + " -- " + var1.getArgument(0).toString().replaceAll("\n", "\\\\n")); 338 | return ""; 339 | }).when(spyFileWriter[0]).write(anyString()); 340 | return spyFileWriter[0]; 341 | }); 342 | when(writerFactoryMock.getFileWriter(eq(FILE_NAME_2))).thenAnswer(i -> { 343 | String filename = i.getArgument(0); 344 | assert(filename.equals(FILE_NAME_2)); 345 | FileWriter fileWriter = new FileWriter(filename, true); 346 | spyFileWriter[1] = spy(fileWriter); 347 | doAnswer((Answer) var1 -> { 348 | System.out.println("WRITING: " + filename + " -- " + var1.getArgument(0).toString().replaceAll("\n", "\\\\n")); 349 | return ""; 350 | }).when(spyFileWriter[1]).write(anyString()); 351 | return spyFileWriter[1]; 352 | }); 353 | 354 | // Test write 355 | AuditLogFileWriter auditLogFileWriter = getNewAuditLogFileWriter(writerFactoryMock, Logger.get("testMultiFileMultiWriteThenClose")); 356 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA_A1)); 357 | auditLogFileWriter.write(FILE_NAME, new SerializedLog(QUERY_ID, DATA_A2)); 358 | auditLogFileWriter.write(FILE_NAME_2, new SerializedLog(QUERY_ID, DATA_B1)); 359 | auditLogFileWriter.write(FILE_NAME_2, new SerializedLog(QUERY_ID, DATA_B2)); 360 | 361 | // Verify 362 | pause(); 363 | verify(spyFileWriter[0], times(1)).write(DATA_A1); 364 | verify(spyFileWriter[0], times(1)).write(DATA_A2); 365 | verify(spyFileWriter[0], times(1)).close(); 366 | verify(spyFileWriter[1], times(1)).write(DATA_B1); 367 | verify(spyFileWriter[1], times(1)).write(DATA_B2); 368 | verify(spyFileWriter[1], times(1)).close(); 369 | } 370 | } 371 | --------------------------------------------------------------------------------