diff --git a/pom.xml b/pom.xml index 9d0844eaf..6a0c4c272 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 pom SCOUTER APM @@ -175,6 +175,7 @@ org.apache.maven.plugins maven-javadoc-plugin + 3.2.0 org.sonatype.plugins diff --git a/scouter.agent.batch/pom.xml b/scouter.agent.batch/pom.xml index ff3692019..0b3148738 100644 --- a/scouter.agent.batch/pom.xml +++ b/scouter.agent.batch/pom.xml @@ -5,7 +5,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-agent-batch diff --git a/scouter.agent.host/pom.xml b/scouter.agent.host/pom.xml index c3f98f285..2340be0e7 100644 --- a/scouter.agent.host/pom.xml +++ b/scouter.agent.host/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-agent-host diff --git a/scouter.agent.java/pom.xml b/scouter.agent.java/pom.xml index 5a5a0c585..53bb75e73 100644 --- a/scouter.agent.java/pom.xml +++ b/scouter.agent.java/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-agent-java @@ -79,6 +79,17 @@ + + + + + + + + + + + @@ -101,6 +112,8 @@ + + @@ -121,6 +134,8 @@ + + @@ -136,7 +151,7 @@ - + @@ -206,6 +221,12 @@ 1.6 1.6 1.6 + + scouter/xtra/reactive/*.java + scouter/xtra/java8/*.java + reactor/**/*.java + com/**/*.java + @@ -340,6 +361,8 @@ scouter.tools.jar scouter.kafka.jar scouter.redis.jar + scouter.reactive.jar + scouter.java8.jar ${project.basedir}/lib/provided/tools.jar ${project.basedir}/lib/provided/java.net.http.jar @@ -361,19 +384,14 @@ org.ow2.asm asm - 7.0 + 8.0.1 org.ow2.asm asm-commons - 7.0 + 8.0.1 - - com.github.scouter-project - byte-buddy-nodep-scouter-repack - 1.7.1.RC1 - jdk.tools jdk.tools @@ -423,13 +441,37 @@ org.springframework spring-web - 4.3.18.RELEASE + 5.2.8.RELEASE + provided + + + org.springframework + spring-webflux + 5.2.8.RELEASE + provided + + + io.projectreactor + reactor-core + 3.3.8.RELEASE + provided + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + 1.3.8 + provided + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.3.72 provided org.springframework spring-core - 4.3.19.RELEASE + 5.2.8.RELEASE provided @@ -438,6 +480,19 @@ 0.10.1.0 provided + + org.elasticsearch.client + elasticsearch-rest-client + 6.8.10 + provided + + + org.mongodb + mongodb-driver-core + 4.0.5 + provided + + io.lettuce lettuce-core @@ -482,7 +537,9 @@ - **/scouter/xtra/** + scouter/xtra/** + reactor/** + com/** diff --git a/scouter.agent.java/src/main/java/com/mongodb/async/SingleResultCallback.java b/scouter.agent.java/src/main/java/com/mongodb/async/SingleResultCallback.java new file mode 100644 index 000000000..0057a7689 --- /dev/null +++ b/scouter.agent.java/src/main/java/com/mongodb/async/SingleResultCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//for mongo driver 3.8 +package com.mongodb.async; + +/** + * An interface to describe the completion of an asynchronous operation. + * + * @param the result type + * @since 3.0 + */ +public interface SingleResultCallback { + /** + * Called when the operation completes. + * @param result the result, which may be null. Always null if e is not null. + * @param t the throwable, or null if the operation completed normally + */ + void onResult(T result, Throwable t); +} diff --git a/scouter.agent.java/src/main/java/com/mongodb/connection/InternalConnection.java b/scouter.agent.java/src/main/java/com/mongodb/connection/InternalConnection.java new file mode 100644 index 000000000..8d629f83f --- /dev/null +++ b/scouter.agent.java/src/main/java/com/mongodb/connection/InternalConnection.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2008-2014 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//for mongo db driver 3.6 +package com.mongodb.connection; + +import com.mongodb.async.SingleResultCallback; +import com.mongodb.internal.connection.CommandMessage; +import com.mongodb.internal.connection.ResponseBuffers; +import com.mongodb.internal.session.SessionContext; +import org.bson.ByteBuf; +import org.bson.codecs.Decoder; + +import java.util.List; + +public interface InternalConnection extends BufferProvider { + + /** + * Gets the description of this connection. + * + * @return the connection description + */ + ConnectionDescription getDescription(); + + /** + * Opens the connection so its ready for use + */ + void open(); + + /** + * Opens the connection so its ready for use + * + * @param callback the callback to be called once the connection has been opened + */ + void openAsync(SingleResultCallback callback); + + /** + * Closes the connection. + */ + void close(); + + /** + * Returns if the connection has been opened + * + * @return true if connection has been opened + */ + boolean opened(); + + /** + * Returns the closed state of the connection + * + * @return true if connection is closed + */ + boolean isClosed(); + + /** + * Send a command message to the server. + * + * @param message the command message to send + * @param sessionContext the session context + */ + T sendAndReceive(CommandMessage message, Decoder decoder, SessionContext sessionContext); + + /** + * Send a command message to the server. + * + * @param message the command message to send + * @param sessionContext the session context + * @param callback the callback + */ + void sendAndReceiveAsync(CommandMessage message, Decoder decoder, SessionContext sessionContext, + SingleResultCallback callback); + + /** + * Send a message to the server. The connection may not make any attempt to validate the integrity of the message. + * + * @param byteBuffers the list of byte buffers to send. + * @param lastRequestId the request id of the last message in byteBuffers + */ + void sendMessage(List byteBuffers, int lastRequestId); + + /** + * Receive a response to a sent message from the server. + * + * @param responseTo the request id that this message is a response to + * @return the response + */ + ResponseBuffers receiveMessage(int responseTo); + + /** + * Asynchronously send a message to the server. The connection may not make any attempt to validate the integrity of the message. + * + * @param byteBuffers the list of byte buffers to send + * @param lastRequestId the request id of the last message in byteBuffers + * @param callback the callback to invoke on completion + */ + void sendMessageAsync(List byteBuffers, int lastRequestId, SingleResultCallback callback); + + /** + * Asynchronously receive a response to a sent message from the server. + * + * @param responseTo the request id that this message is a response to + * @param callback the callback to invoke on completion + */ + void receiveMessageAsync(int responseTo, SingleResultCallback callback); + +} diff --git a/scouter.agent.java/src/main/java/com/mongodb/connection/SplittablePayload.java b/scouter.agent.java/src/main/java/com/mongodb/connection/SplittablePayload.java new file mode 100644 index 000000000..dd33c1305 --- /dev/null +++ b/scouter.agent.java/src/main/java/com/mongodb/connection/SplittablePayload.java @@ -0,0 +1,168 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.connection; + +import org.bson.BsonDocument; + +import java.util.List; + +import static com.mongodb.assertions.Assertions.isTrue; +import static com.mongodb.assertions.Assertions.notNull; +import static com.mongodb.connection.SplittablePayload.Type.INSERT; +import static com.mongodb.connection.SplittablePayload.Type.REPLACE; +import static com.mongodb.connection.SplittablePayload.Type.UPDATE; + +/** + * A Splittable payload for write commands. + * + *

The command will consume as much of the payload as possible. The {@link #hasAnotherSplit()} method will return true if there is + * another split to consume, {@link #getNextSplit} method will return the next SplittablePayload.

+ * + * @see com.mongodb.connection.Connection#command(String, org.bson.BsonDocument, org.bson.FieldNameValidator, com.mongodb.ReadPreference, + * org.bson.codecs.Decoder, com.mongodb.session.SessionContext, boolean, com.mongodb.connection.SplittablePayload, + * org.bson.FieldNameValidator) + * @see com.mongodb.connection.AsyncConnection#commandAsync(String, org.bson.BsonDocument, org.bson.FieldNameValidator, + * com.mongodb.ReadPreference, org.bson.codecs.Decoder, com.mongodb.session.SessionContext, boolean, + * com.mongodb.connection.SplittablePayload, org.bson.FieldNameValidator, com.mongodb.async.SingleResultCallback) + * @since 3.6 + */ + +//for mongo driver 3.8 +public final class SplittablePayload { + private final Type payloadType; + private final List payload; + private int position = 0; + + /** + * The type of the payload. + */ + public enum Type { + /** + * An insert. + */ + INSERT, + + /** + * An update that uses update operators. + */ + UPDATE, + + /** + * An update that replaces the existing document. + */ + REPLACE, + + /** + * A delete. + */ + DELETE + } + + /** + * Create a new instance + * + * @param payloadType the payload type + * @param payload the payload + */ + public SplittablePayload(final Type payloadType, final List payload) { + this.payloadType = notNull("batchType", payloadType); + this.payload = notNull("payload", payload); + } + + /** + * @return the payload type + */ + public Type getPayloadType() { + return payloadType; + } + + /** + * @return the payload name + */ + public String getPayloadName() { + if (payloadType == INSERT) { + return "documents"; + } else if (payloadType == UPDATE || payloadType == REPLACE) { + return "updates"; + } else { + return "deletes"; + } + } + + /** + * @return the payload + */ + public List getPayload() { + return payload; + } + + /** + * @return the current position in the payload + */ + public int getPosition() { + return position; + } + + /** + * Sets the current position in the payload + * @param position the position + */ + public void setPosition(final int position) { + this.position = position; + } + + /** + * @return true if there are more values after the current position + */ + public boolean hasAnotherSplit() { + return payload.size() > position; + } + + /** + * @return a new SplittablePayload containing only the values after the current position. + */ + public SplittablePayload getNextSplit() { + isTrue("hasAnotherSplit", hasAnotherSplit()); + List nextPayLoad = payload.subList(position, payload.size()); + return new SplittablePayload(payloadType, nextPayLoad); + } + + /** + * @return true if the payload is empty + */ + public boolean isEmpty() { + return payload.isEmpty(); + } +} diff --git a/scouter.agent.java/src/main/java/reactor/core/publisher/ScouterOptimizableOperatorProxy.java b/scouter.agent.java/src/main/java/reactor/core/publisher/ScouterOptimizableOperatorProxy.java new file mode 100644 index 000000000..77c61b493 --- /dev/null +++ b/scouter.agent.java/src/main/java/reactor/core/publisher/ScouterOptimizableOperatorProxy.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package reactor.core.publisher; + +import scouter.agent.Logger; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/08 + */ +public class ScouterOptimizableOperatorProxy { + + public static final String EMPTY = ""; + public static boolean accessible = false; + public static boolean first = true; + + public static String nameOnCheckpoint(Object candidate) { + try { + if (!accessible && first) { + try { + Class checker = Class.forName("reactor.core.publisher.OptimizableOperator"); + accessible = true; + } catch (ClassNotFoundException e) { + accessible = false; + Logger.println("reactor.core.publisher.OptimizableOperator not accessible. reactor checkpoint processing will be disabled."); + } + first = false; + } + if (!accessible) { + return EMPTY; + } + + if (candidate instanceof OptimizableOperator) { + OptimizableOperator operator = ((OptimizableOperator) candidate).nextOptimizableSource(); + if (operator == null) { + return EMPTY; + } + if (operator instanceof MonoOnAssembly) { + FluxOnAssembly.AssemblySnapshot snapshot = ((MonoOnAssembly) operator).stacktrace; + if (snapshot != null && snapshot.checkpointed) { + return snapshot.cached; + } + } else if (operator instanceof FluxOnAssembly) { + FluxOnAssembly.AssemblySnapshot snapshot = ((FluxOnAssembly) operator).snapshotStack; + if (snapshot != null && snapshot.checkpointed) { + return snapshot.cached; + } + } + } + return EMPTY; + } catch (Throwable e) { + + return EMPTY; + } + } + + public static void appendSources4Dump(Object candidate, StringBuilder builder) { + try { + if (!accessible && first) { + try { + Class checker = Class.forName("reactor.core.publisher.OptimizableOperator"); + accessible = true; + } catch (ClassNotFoundException e) { + accessible = false; + Logger.println("reactor.core.publisher.OptimizableOperator not accessible. reactor checkpoint processing will be disabled."); + } + first = false; + } + if (!accessible) { + return; + } + + if (candidate instanceof OptimizableOperator) { + OptimizableOperator operator = ((OptimizableOperator) candidate).nextOptimizableSource(); + if (operator == null) { + return; + } + String p1 = operator.toString(); + builder.append(" (<-) ").append(p1); + if (p1.startsWith("checkpoint")) { + OptimizableOperator operator2 = operator.nextOptimizableSource(); + if (operator2 != null) { + builder.append(" (<-) ").append(operator2.toString()); + } + } + } + } catch (Exception e) { + Logger.println("R01o2", e.getMessage(), e); + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/AgentCommonConstant.java b/scouter.agent.java/src/main/java/scouter/agent/AgentCommonConstant.java index 92b7ba4dd..725712b52 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/AgentCommonConstant.java +++ b/scouter.agent.java/src/main/java/scouter/agent/AgentCommonConstant.java @@ -12,6 +12,10 @@ public class AgentCommonConstant { public static final String REQUEST_ATTRIBUTE_CALLER_TRANSFER_MAP = "__scouter__ctm__"; public static final String REQUEST_ATTRIBUTE_ALL_DISPATCHED_TRACE_CONTEXT = "__scouter__adtc__"; public static final String REQUEST_ATTRIBUTE_SELF_DISPATCHED = "__scouter__sd__"; + public static final String TRACE_ID = "__scouter__txid__"; + public static final String TRACE_CONTEXT = "__scouter__tctx__"; + public static final String SUBS_DEPTH = "__scouter__subdepth__"; + public static final String SCOUTER_ADDED_FIELD = "__scouter__added__"; public static final String ASYNC_SERVLET_DISPATCHED_PREFIX = "f>"; diff --git a/scouter.agent.java/src/main/java/scouter/agent/AgentTransformer.java b/scouter.agent.java/src/main/java/scouter/agent/AgentTransformer.java index 6b0ade38b..e8f79f62a 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/AgentTransformer.java +++ b/scouter.agent.java/src/main/java/scouter/agent/AgentTransformer.java @@ -28,9 +28,12 @@ import scouter.agent.asm.ApicallJavaHttpRequestASM; import scouter.agent.asm.ApicallSpringHandleResponseASM; import scouter.agent.asm.ApicallSpringHttpAccessorASM; +import scouter.agent.asm.ApicallWebClientInfoASM; +import scouter.agent.asm.ApicallWebClientResponseASM; import scouter.agent.asm.CapArgsASM; import scouter.agent.asm.CapReturnASM; import scouter.agent.asm.CapThisASM; +import scouter.agent.asm.HttpReactiveServiceASM; import scouter.agent.asm.HttpServiceASM; import scouter.agent.asm.IASM; import scouter.agent.asm.InitialContextASM; @@ -53,18 +56,26 @@ import scouter.agent.asm.UserTxASM; import scouter.agent.asm.asyncsupport.AsyncContextDispatchASM; import scouter.agent.asm.asyncsupport.CallRunnableASM; +import scouter.agent.asm.asyncsupport.CoroutineThreadNameASM; import scouter.agent.asm.asyncsupport.HystrixCommandASM; +import scouter.agent.asm.asyncsupport.MonoKtASM; import scouter.agent.asm.asyncsupport.RequestStartAsyncASM; +import scouter.agent.asm.asyncsupport.ThreadASM; import scouter.agent.asm.asyncsupport.executor.ExecutorServiceASM; import scouter.agent.asm.asyncsupport.spring.SpringAsyncExecutionASM; import scouter.agent.asm.asyncsupport.spring.SpringAsyncExecutionAspectSupportDoSubmitASM; +import scouter.agent.asm.elasticsearch.HttpNioEntityASM; +import scouter.agent.asm.elasticsearch.RestClientASM; import scouter.agent.asm.kafka.KafkaProducerASM; +import scouter.agent.asm.mongodb.MongoCommandProtocolASM; import scouter.agent.asm.rabbit.RabbitPublisherASM; import scouter.agent.asm.redis.JedisCommandASM; import scouter.agent.asm.redis.JedisProtocolASM; import scouter.agent.asm.redis.LettuceASM; import scouter.agent.asm.redis.RedisCacheKeyASM; import scouter.agent.asm.redis.RedisKeyASM; +import scouter.agent.asm.test.MongoModifyASM; +import scouter.agent.asm.test.ReactorModifyASM; import scouter.agent.asm.util.AsmUtil; import scouter.agent.util.AsyncRunner; import scouter.lang.conf.ConfObserver; @@ -103,8 +114,20 @@ public void run() { public static void reload() { Configure conf = Configure.getInstance(); List temp = new ArrayList(); + temp.add(new ReactorModifyASM()); + temp.add(new MongoModifyASM()); + temp.add(new MongoCommandProtocolASM()); + + temp.add(new ThreadASM()); temp.add(new HttpServiceASM()); temp.add(new ServiceASM()); + temp.add(new HttpReactiveServiceASM()); + temp.add(new CoroutineThreadNameASM()); + temp.add(new MonoKtASM()); + temp.add(new ApicallWebClientInfoASM()); + temp.add(new ApicallWebClientResponseASM()); + temp.add(new HttpNioEntityASM()); + temp.add(new RestClientASM()); temp.add(new RequestStartAsyncASM()); temp.add(new AsyncContextDispatchASM()); @@ -206,7 +229,7 @@ public byte[] transform(final ClassLoader loader, String className, final Class ObjTypeDetector.check(className); final ClassDesc classDesc = new ClassDesc(); ClassReader cr = new ClassReader(classfileBuffer); - cr.accept(new ClassVisitor(Opcodes.ASM7) { + cr.accept(new ClassVisitor(Opcodes.ASM8) { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { classDesc.set(version, access, name, signature, superName, interfaces); @@ -222,9 +245,13 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return super.visitAnnotation(desc, visible); } }, 0); - if (AsmUtil.isInterface(classDesc.access)) { + if (AsmUtil.isInterface(classDesc.access) + && !"reactor/core/publisher/OptimizableOperator".equals(className) + && !"com/mongodb/connection/InternalConnection".equals(className) + ) { return null; } + classDesc.classBeingRedefined = classBeingRedefined; ClassWriter cw = getClassWriter(classDesc); ClassVisitor cv = cw; diff --git a/scouter.agent.java/src/main/java/scouter/agent/Configure.java b/scouter.agent.java/src/main/java/scouter/agent/Configure.java index b66f20e56..28c676ba0 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/Configure.java +++ b/scouter.agent.java/src/main/java/scouter/agent/Configure.java @@ -193,6 +193,14 @@ public static final Configure getInstance() { @ConfigDesc("") public boolean profile_fullstack_stmt_leak_enabled = false; + @ConfigDesc("Profile elastic search full query.\nIt need more payload and disk usage.") + public boolean profile_elasticsearch_full_query_enabled = false; + + @ConfigDesc("profile reactor's important checkpoint") + public boolean profile_reactor_checkpoint_enabled = true; + @ConfigDesc("profile reactor's another checkpoints") + public boolean profile_reactor_more_checkpoint_enabled = false; + //Trace @ConfigDesc("User ID based(0 : Remote IP Address, 1 : Cookie(JSESSIONID), 2 : Cookie(SCOUTER), 3 : Header) \n - able to set value for 1.Cookie and 3.Header \n - refer to 'trace_user_session_key'") public int trace_user_mode = 2; // 0:Remote IP, 1:JSessionID, 2:Scouter Cookie, 3:Header @@ -334,6 +342,10 @@ public static final Configure getInstance() { public boolean xlog_error_on_apicall_exception_enabled = true; @ConfigDesc("mark as error on xlog flag if redis error is occured.") public boolean xlog_error_on_redis_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if elasticsearc error is occured.") + public boolean xlog_error_on_elasticsearch_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if mongodb error is occured.") + public boolean xlog_error_on_mongodb_exception_enabled = true; //XLog hard sampling options @ConfigDesc("XLog hard sampling mode enabled\n - for the best performance but it affects all statistics data") @@ -344,6 +356,11 @@ public static final Configure getInstance() { //XLog soft sampling options @ConfigDesc("XLog sampling - ignore global consequent sampling. the commencement service's sampling option affects it's children.") public boolean ignore_global_consequent_sampling = false; + @ConfigDesc("XLog sampling - The service of this patterns can be unsampled by the sampling rate even if parent call is sampled and on tracing.") + public String xlog_consequent_sampling_ignore_patterns= ""; + + @ConfigDesc("XLog sampling exclude patterns.") + public String xlog_sampling_exclude_patterns = ""; @ConfigDesc("XLog sampling mode enabled") public boolean xlog_sampling_enabled = false; @@ -668,9 +685,6 @@ public static final Configure getInstance() { @ConfigDesc("hook threadpool executor for tracing async processing.") public boolean hook_async_thread_pool_executor_enabled = true; - @ConfigDesc("Experimental! test it on staging environment of your system before enable this option.\n enable lambda expressioned class hook for detecting asyncronous processing. \nOnly classes under the package configured by 'hook_async_callrunnable_scan_package_prefixes' is hooked.") - public boolean hook_lambda_instrumentation_strategy_enabled = false; - @ConfigDesc("hystrix execution hook enabled") public boolean hook_hystrix_enabled = false; @@ -703,7 +717,19 @@ public static final Configure getInstance() { @ConfigDesc("") public boolean _hook_kafka_enabled = true; @ConfigDesc("") + public boolean _hook_elasticsearch_enabled = true; + @ConfigDesc("") + public boolean hook_mongodb_enabled = false; + @ConfigDesc("") public boolean _hook_rabbit_enabled = true; + @ConfigDesc("") + public boolean _hook_reactive_enabled = true; + @ConfigDesc("") + public boolean _hook_coroutine_enabled = true; + @ConfigDesc("") + public boolean _hook_coroutine_debugger_hook_enabled = false; + @ConfigDesc("") + public boolean _hook_thread_name_enabled = false; @ConfigDesc("") public String _hook_direct_patch_classes = ""; @@ -750,6 +776,7 @@ public static final Configure getInstance() { public boolean _psts_enabled = false; @ConfigDesc("PSTS(periodical stacktrace step) thread dump Interval(ms) - hard min limit 2000") public int _psts_dump_interval_ms = 10000; + public boolean _psts_progressive_reactor_thread_trace_enabled = true; //Summary @ConfigDesc("Activating summary function") @@ -1015,7 +1042,6 @@ private void apply() { this.hook_async_thread_pool_executor_enabled = getBoolean("hook_async_thread_pool_executor_enabled", true); - this.hook_lambda_instrumentation_strategy_enabled = getBoolean("hook_lambda_instrumentation_strategy_enabled", false); this.hook_hystrix_enabled = getBoolean("hook_hystrix_enabled", true); this.hook_add_fields = getValue("hook_add_fields", ""); @@ -1058,6 +1084,11 @@ private void apply() { this.profile_fullstack_rs_leak_enabled = getBoolean("profile_fullstack_rs_leak_enabled", false); this.profile_fullstack_stmt_leak_enabled = getBoolean("profile_fullstack_stmt_leak_enabled", false); + this.profile_elasticsearch_full_query_enabled = getBoolean("profile_elasticsearch_full_query_enabled", false); + + this.profile_reactor_checkpoint_enabled = getBoolean("profile_reactor_checkpoint_enabled", true); + this.profile_reactor_more_checkpoint_enabled = getBoolean("profile_reactor_more_checkpoint_enabled", false); + this.net_udp_collection_interval_ms = getInt("net_udp_collection_interval_ms", 100); this.trace_http_client_ip_header_key = getValue("trace_http_client_ip_header_key", ""); @@ -1091,7 +1122,13 @@ private void apply() { this._hook_spring_rest_enabled = getBoolean("_hook_spring_rest_enabled", true); this._hook_redis_enabled = getBoolean("_hook_redis_enabled", true); this._hook_kafka_enabled = getBoolean("_hook_kafka_enabled", true); + this._hook_elasticsearch_enabled = getBoolean("_hook_elasticsearch_enabled", true); + this.hook_mongodb_enabled = getBoolean("hook_mongodb_enabled", false); this._hook_rabbit_enabled = getBoolean("_hook_rabbit_enabled", true); + this._hook_reactive_enabled = getBoolean("_hook_reactive_enabled", true); + this._hook_coroutine_enabled = getBoolean("_hook_coroutine_enabled", true); + this._hook_coroutine_debugger_hook_enabled = getBoolean("_hook_coroutine_debugger_hook_enabled", false); + this._hook_thread_name_enabled = getBoolean("_hook_thread_name_enabled", false); this._hook_direct_patch_classes = getValue("_hook_direct_patch_classes", ""); @@ -1109,6 +1146,7 @@ private void apply() { this._psts_enabled = getBoolean("_psts_enabled", false); this._psts_dump_interval_ms = getInt("_psts_dump_interval_ms", 10000); + this._psts_progressive_reactor_thread_trace_enabled = getBoolean("_psts_progressive_reactor_dump_enabled", true); // 웹시스템으로 부터 WAS 사이의 성능과 어떤 웹서버가 요청을 보내 왔는지를 추적하는 기능을 ON/OFF하고 // 관련 키정보를 지정한다. @@ -1156,6 +1194,8 @@ private void apply() { this.xlog_error_on_sqlexception_enabled = getBoolean("xlog_error_on_sqlexception_enabled", true); this.xlog_error_on_apicall_exception_enabled = getBoolean("xlog_error_on_apicall_exception_enabled", true); this.xlog_error_on_redis_exception_enabled = getBoolean("xlog_error_on_redis_exception_enabled", true); + this.xlog_error_on_elasticsearch_exception_enabled = getBoolean("xlog_error_on_elasticsearch_exception_enabled", true); + this.xlog_error_on_mongodb_exception_enabled = getBoolean("xlog_error_on_mongodb_exception_enabled", true); this._log_asm_enabled = getBoolean("_log_asm_enabled", false); this.obj_type_inherit_to_child_enabled = getBoolean("obj_type_inherit_to_child_enabled", false); @@ -1180,6 +1220,9 @@ private void apply() { this._xlog_hard_sampling_rate_pct = getInt("_xlog_hard_sampling_rate_pct", 10); this.ignore_global_consequent_sampling = getBoolean("ignore_global_consequent_sampling", false); + this.xlog_consequent_sampling_ignore_patterns = getValue("xlog_consequent_sampling_ignore_patterns", ""); + + this.xlog_sampling_exclude_patterns = getValue("xlog_sampling_exclude_patterns", ""); this.xlog_sampling_enabled = getBoolean("xlog_sampling_enabled", false); this.xlog_sampling_only_profile = getBoolean("xlog_sampling_only_profile", false); diff --git a/scouter.agent.java/src/main/java/scouter/agent/DirectPatch.java b/scouter.agent.java/src/main/java/scouter/agent/DirectPatch.java index 42033696f..b710ff1c1 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/DirectPatch.java +++ b/scouter.agent.java/src/main/java/scouter/agent/DirectPatch.java @@ -51,7 +51,7 @@ private static String getClassName(byte[] bytes) { try { final ClassDesc classDesc = new ClassDesc(); ClassReader cr = new ClassReader(bytes); - cr.accept(new ClassVisitor(Opcodes.ASM7) { + cr.accept(new ClassVisitor(Opcodes.ASM8) { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { classDesc.set(version, access, name, signature, superName, interfaces); diff --git a/scouter.agent.java/src/main/java/scouter/agent/JavaAgent.java b/scouter.agent.java/src/main/java/scouter/agent/JavaAgent.java index 4abbf23e7..e76faea1d 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/JavaAgent.java +++ b/scouter.agent.java/src/main/java/scouter/agent/JavaAgent.java @@ -18,7 +18,6 @@ import scouter.agent.netio.data.net.TcpRequestMgr; import scouter.agent.util.AsyncRunner; -import scouter.repack.net.bytebuddy.agent.builder.AgentBuilder; import scouter.util.StringSet; import scouter.util.logo.Logo; @@ -43,6 +42,10 @@ public class JavaAgent { } public static void premain(String options, Instrumentation instrum) { + Configure conf = Configure.getInstance(); + if (conf._hook_coroutine_debugger_hook_enabled && System.getProperty("kotlinx.coroutines.debug") == null) { + System.setProperty("kotlinx.coroutines.debug", ""); + } preStart(options, instrum, new AgentTransformer()); } @@ -53,30 +56,11 @@ public static void preStart(String options, Instrumentation instrum, ClassFileTr intro(); Configure conf = Configure.getInstance(); - if(conf.hook_lambda_instrumentation_strategy_enabled) { - Logger.println("LD001", "hook_lambda_instrumentation_strategy_enabled = true!"); - Logger.println("LD001", "This feature is very experimental !!\n test it in your test environment first !!"); - - try { - new AgentBuilder.Default() - .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) - .installOn(instrum); - } catch (Throwable t) { - Logger.println("LD002", "scouter min version doesn't support this feature !!!"); - Logger.println("LD002", "Fail to hook_lambda_instrumentation_strategy_enabled !"); - Logger.println("LD003", "Fatal on load bytebuddy AgentBuilder", t); - } - } BackJobs.getInstance().put(Logger.class.getName(), 3000, Logger.initializer); JavaAgent.instrumentation = instrum; JavaAgent.instrumentation.addTransformer(transformer); - //TODO suuport 1.5 ? - //JavaAgent.instrumentation.addTransformer(transformer, true); - - // RequestAgent.getInstance(); - addAsyncRedefineClasses(); TcpRequestMgr.getInstance(); @@ -95,6 +79,7 @@ private static void addAsyncRedefineClasses() { redefineClasses.put("java.util.concurrent.AbstractExecutorService"); redefineClasses.put("java.util.concurrent.ThreadPoolExecutor"); + redefineClasses.put("java.lang.Thread"); //java.lang.invoke.LambdaMetafactory.*,java.lang.invoke.CallSite.*, //java.lang.invoke.ConstantCallSite.*, @@ -109,8 +94,6 @@ private static void addAsyncRedefineClasses() { // redefineClasses.put("java.lang.invoke.DirectMethodHandle"); AsyncRunner.getInstance().add(redefineClasses); - - } private static void intro() { diff --git a/scouter.agent.java/src/main/java/scouter/agent/LambdaFormTransformer.java b/scouter.agent.java/src/main/java/scouter/agent/LambdaFormTransformer.java index cd51cf70c..b8b839836 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/LambdaFormTransformer.java +++ b/scouter.agent.java/src/main/java/scouter/agent/LambdaFormTransformer.java @@ -71,7 +71,7 @@ public byte[] transform(final ClassLoader loader, String className, final Class final ClassDesc classDesc = new ClassDesc(); ClassReader cr = new ClassReader(classfileBuffer); - cr.accept(new ClassVisitor(Opcodes.ASM7) { + cr.accept(new ClassVisitor(Opcodes.ASM8) { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { classDesc.set(version, access, name, signature, superName, interfaces); diff --git a/scouter.agent.java/src/main/java/scouter/agent/Logger.java b/scouter.agent.java/src/main/java/scouter/agent/Logger.java index c1edc9de2..c54e92323 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/Logger.java +++ b/scouter.agent.java/src/main/java/scouter/agent/Logger.java @@ -59,6 +59,9 @@ private static String toString(Object message) { } private static String build(String id, String message) { + if (message == null) { + return "null-err-message"; + } return new StringBuffer(20 + id.length() + message.length()) .append(DateUtil.datetime(System.currentTimeMillis())).append(" [").append(id).append("] ") .append(message).toString(); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/AddFieldASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/AddFieldASM.java index 77a288975..f4ef068ff 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/AddFieldASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/AddFieldASM.java @@ -26,9 +26,14 @@ import scouter.agent.asm.util.HookingSet; import java.util.Map; + +import static scouter.agent.AgentCommonConstant.SCOUTER_ADDED_FIELD; + public class AddFieldASM implements IASM, Opcodes { public final Map target = HookingSet.getClassFieldSet(Configure.getInstance().hook_add_fields); public AddFieldASM() { + target.put("org/springframework/web/reactive/function/client/DefaultClientRequestBuilder$BodyInserterRequest", + SCOUTER_ADDED_FIELD); } Configure conf = Configure.getInstance(); public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { @@ -46,7 +51,7 @@ class AddFieldCV extends ClassVisitor implements Opcodes { private String field; private String className; public AddFieldCV(ClassVisitor cv, String className, String field) { - super(ASM7, cv); + super(ASM8, cv); this.field = field; this.className = className; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApiCallResponseObjectASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApiCallResponseObjectASM.java index 8b6c8e610..a6e08e1d0 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ApiCallResponseObjectASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApiCallResponseObjectASM.java @@ -58,7 +58,7 @@ class ApiCallResponseObjectCV extends ClassVisitor implements Opcodes { String className; public ApiCallResponseObjectCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -89,7 +89,7 @@ class ApiCallResponseObjectInitMV extends LocalVariablesSorter implements Opcode private Label startFinally = new Label(); public ApiCallResponseObjectInitMV(String className, int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.className = className; this.name = name; this.desc = desc; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallASM.java index 912411c95..8c975089f 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallASM.java @@ -16,10 +16,15 @@ */ package scouter.agent.asm; -import org.objectweb.asm.*; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.objectweb.asm.commons.LocalVariablesSorter; import scouter.agent.ClassDesc; import scouter.agent.Configure; +import scouter.agent.Logger; import scouter.agent.asm.util.AsmUtil; import scouter.agent.asm.util.HookingSet; import scouter.agent.trace.TraceApiCall; @@ -27,12 +32,15 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; public class ApicallASM implements IASM, Opcodes { private List target = HookingSet.getHookingMethodSet(Configure.getInstance().hook_apicall_patterns); private Map reserved = new HashMap(); + protected static Set onlyStartClass = new HashSet(); public static class ApiCallTargetRegister { public static final List> klassMethod = new ArrayList>(); @@ -83,9 +91,13 @@ public ApicallASM() { "Ljava/net/http/HttpResponse$BodyHandler;" + ")Ljava/net/http/HttpResponse;"); + AsmUtil.add(reserved, "org/springframework/web/reactive/function/client/ExchangeFunctions$DefaultExchangeFunction", "exchange(" + + "Lorg/springframework/web/reactive/function/client/ClientRequest;" + + ")Lreactor/core/publisher/Mono;"); for(int i = ApiCallTargetRegister.klassMethod.size() - 1; i >= 0; i--) { AsmUtil.add(reserved, ApiCallTargetRegister.klassMethod.get(i).getLeft(), ApiCallTargetRegister.klassMethod.get(i).getRight()); } + onlyStartClass.add("org/springframework/web/reactive/function/client/ExchangeFunctions$DefaultExchangeFunction"); } public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { @@ -110,7 +122,7 @@ class ApicallExtCV extends ClassVisitor implements Opcodes { private HookingSet mset; public ApicallExtCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -124,7 +136,7 @@ public MethodVisitor visitMethod(int access, String methodName, String desc, Str if (AsmUtil.isSpecial(methodName)) { return mv; } - //Logger.println("apicall: " + className + "." + methodName + desc); + Logger.println("apicall: " + className + "." + methodName + desc); return new ApicallExtMV(access, desc, mv, Type.getArgumentTypes(desc), (access & ACC_STATIC) != 0, className, methodName, desc); } @@ -140,7 +152,7 @@ class ApicallExtMV extends LocalVariablesSorter implements Opcodes { public ApicallExtMV(int access, String desc, MethodVisitor mv, Type[] paramTypes, boolean isStatic, String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.paramTypes = paramTypes; this.returnType = Type.getReturnType(desc); this.isStatic = isStatic; @@ -234,6 +246,9 @@ public void visitInsn(int opcode) { } private void capReturn() { + if (ApicallASM.onlyStartClass.contains(className)) { + return; + } Type tp = returnType; if (tp == null || tp.equals(Type.VOID_TYPE)) { mv.visitVarInsn(Opcodes.ALOAD, statIdx); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallInfoASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallInfoASM.java index 794384ece..fbdfeb1b9 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallInfoASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallInfoASM.java @@ -59,7 +59,7 @@ class ApicallInfoCV extends ClassVisitor implements Opcodes { public String className; private HookingSet mset; public ApicallInfoCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -84,7 +84,7 @@ class ApicallInfoMV extends LocalVariablesSorter implements Opcodes { private static final String START_SIGNATURE = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V"; public ApicallInfoMV(int access, String desc, MethodVisitor mv, Type[] paramTypes, boolean isStatic, String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.paramTypes = paramTypes; this.isStatic = isStatic; this.className = classname; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallJavaHttpRequestASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallJavaHttpRequestASM.java index 5c4842881..76510c1f1 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallJavaHttpRequestASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallJavaHttpRequestASM.java @@ -35,7 +35,7 @@ class ApicallJavaHttpRequestCV extends ClassVisitor implements Opcodes { public String className; private HookingSet mset; public ApicallJavaHttpRequestCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -58,7 +58,7 @@ class ApicallJavaHttpRequestMV extends LocalVariablesSorter implements Opcodes { String desc; public ApicallJavaHttpRequestMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHandleResponseASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHandleResponseASM.java index f9723a13d..e4efc2341 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHandleResponseASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHandleResponseASM.java @@ -58,7 +58,7 @@ class RestTemplateResponseHandlerCV extends ClassVisitor implements Opcodes { public String className; private HookingSet mset; public RestTemplateResponseHandlerCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -86,7 +86,7 @@ class RestTemplateResponseHandlerMV extends LocalVariablesSorter implements Opco int respIdx; public RestTemplateResponseHandlerMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.respIdx = AsmUtil.getIdxByType(access, desc, Type.getType("Lorg/springframework/http/client/ClientHttpResponse;")); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHttpAccessorASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHttpAccessorASM.java index a5116e463..40da1b57a 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHttpAccessorASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallSpringHttpAccessorASM.java @@ -54,7 +54,7 @@ class HttpAccessorCV extends ClassVisitor implements Opcodes { public String className; private HookingSet mset; public HttpAccessorCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -84,7 +84,7 @@ class CreateRequestMV extends LocalVariablesSorter implements Opcodes { private Type returnType; public CreateRequestMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallWebClientInfoASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallWebClientInfoASM.java new file mode 100644 index 000000000..9ed49a6a1 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallWebClientInfoASM.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package scouter.agent.asm; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.util.AsmUtil; +import scouter.agent.asm.util.HookingSet; +import scouter.agent.trace.TraceApiCall; + +import java.util.HashMap; +import java.util.Map; + +public class ApicallWebClientInfoASM implements IASM, Opcodes { + + private Map reserved = new HashMap(); + + public ApicallWebClientInfoASM() { + AsmUtil.add(reserved, "org/springframework/web/reactive/function/client/DefaultClientRequestBuilder$BodyInserterRequest", + "writeTo(Lorg/springframework/http/client/reactive/ClientHttpRequest;" + + "Lorg/springframework/web/reactive/function/client/ExchangeStrategies;)" + + "Lreactor/core/publisher/Mono;"); + } + + @Override + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + Configure conf = Configure.getInstance(); + if (!conf._hook_apicall_enabled) { + return cv; + } + if (conf._hook_reactive_enabled == false) { + return cv; + } + + HookingSet mset = reserved.get(className); + if (mset != null) + return new WebClientRequestBuilderBodyInserterCV(cv, mset, className); + return cv; + } +} + +class WebClientRequestBuilderBodyInserterCV extends ClassVisitor implements Opcodes { + public String className; + private HookingSet mset; + public WebClientRequestBuilderBodyInserterCV(ClassVisitor cv, HookingSet mset, String className) { + super(ASM8, cv); + this.mset = mset; + this.className = className; + } + @Override + public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions); + if (mv == null || mset.isA(methodName, desc) == false) { + return mv; + + } + if (AsmUtil.isSpecial(methodName)) { + return mv; + } + return new RequestBuilderBodyInserterWriteToMV(access, methodName, desc, mv); + } +} + +class RequestBuilderBodyInserterWriteToMV extends LocalVariablesSorter implements Opcodes { + private static final String TARGET = TraceApiCall.class.getName().replace('.', '/'); + private static final String START_METHOD = "webClientInfo"; + private static final String START_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;)V"; + + String name; + String desc; + int respIdx; + + public RequestBuilderBodyInserterWriteToMV(int access, String name, String desc, MethodVisitor mv) { + super(ASM8, access, desc, mv); + this.name = name; + this.desc = desc; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, START_METHOD, START_SIGNATURE, false); + + mv.visitCode(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallWebClientResponseASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallWebClientResponseASM.java new file mode 100644 index 000000000..74e4af1a6 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ApicallWebClientResponseASM.java @@ -0,0 +1,104 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package scouter.agent.asm; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.util.AsmUtil; +import scouter.agent.asm.util.HookingSet; +import scouter.agent.trace.TraceApiCall; + +import java.util.HashMap; +import java.util.Map; + +public class ApicallWebClientResponseASM implements IASM, Opcodes { + + private Map reserved = new HashMap(); + + public ApicallWebClientResponseASM() { + AsmUtil.add(reserved, "org/springframework/web/reactive/function/client/ExchangeFunctions$DefaultExchangeFunction", + "logResponse(Lorg/springframework/http/client/reactive/ClientHttpResponse;Ljava/lang/String;)V"); + } + + @Override + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + Configure conf = Configure.getInstance(); + if (!conf._hook_apicall_enabled) { + return cv; + } + if (conf._hook_reactive_enabled == false) { + return cv; + } + + HookingSet mset = reserved.get(className); + if (mset != null) + return new WebClientResponseLogCV(cv, mset, className); + return cv; + } +} + +class WebClientResponseLogCV extends ClassVisitor implements Opcodes { + public String className; + private HookingSet mset; + public WebClientResponseLogCV(ClassVisitor cv, HookingSet mset, String className) { + super(ASM8, cv); + this.mset = mset; + this.className = className; + } + @Override + public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions); + if (mv == null || mset.isA(methodName, desc) == false) { + return mv; + + } + if (AsmUtil.isSpecial(methodName)) { + return mv; + } + return new WebClientResponseLogMV(access, methodName, desc, mv); + } +} + +class WebClientResponseLogMV extends LocalVariablesSorter implements Opcodes { + private static final String TARGET = TraceApiCall.class.getName().replace('.', '/'); + private static final String START_METHOD = "endWebClientApicall"; + private static final String START_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;)V"; + + String name; + String desc; + int respIdx; + + public WebClientResponseLogMV(int access, String name, String desc, MethodVisitor mv) { + super(ASM8, access, desc, mv); + this.name = name; + this.desc = desc; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, START_METHOD, START_SIGNATURE, false); + + mv.visitCode(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/CapArgsASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/CapArgsASM.java index 2ed1f7099..a1bf98dc0 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/CapArgsASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/CapArgsASM.java @@ -54,7 +54,7 @@ class CapArgsCV extends ClassVisitor implements Opcodes { private HookingSet mset; public CapArgsCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -88,7 +88,7 @@ class CapArgsMV extends LocalVariablesSorter implements Opcodes { public CapArgsMV(int access, String desc, MethodVisitor mv, Type[] paramTypes, boolean isStatic, String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.paramTypes = paramTypes; this.isStatic = isStatic; this.className = classname; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/CapReturnASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/CapReturnASM.java index c9d211218..f6cfeed37 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/CapReturnASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/CapReturnASM.java @@ -56,7 +56,7 @@ class CapReturnCV extends ClassVisitor implements Opcodes { private HookingSet mset; public CapReturnCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -91,7 +91,7 @@ public CapReturnMV(int access, String desc, MethodVisitor mv, String classname, String methodname, String methoddesc, boolean isStatic) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.returnType = Type.getReturnType(desc); this.className = classname; this.methodName = methodname; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/CapThisASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/CapThisASM.java index 61720133f..47f60b805 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/CapThisASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/CapThisASM.java @@ -52,7 +52,7 @@ class CapThisCV extends ClassVisitor implements Opcodes { private String className; public CapThisCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -77,7 +77,7 @@ class CapThisMV extends MethodVisitor implements Opcodes { private String methodDesc; public CapThisMV(String classname, String methoddesc, MethodVisitor mv) { - super(ASM7, mv); + super(ASM8, mv); this.className = classname; this.methodDesc = methoddesc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/HttpReactiveServiceASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/HttpReactiveServiceASM.java new file mode 100644 index 000000000..ce5a3fc06 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/HttpReactiveServiceASM.java @@ -0,0 +1,183 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package scouter.agent.asm; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.trace.TraceMain; + +import java.util.HashSet; + +public class HttpReactiveServiceASM implements IASM, Opcodes { + public HashSet handlers = new HashSet(); +// public HashSet handlersRes = new HashSet(); + public HttpReactiveServiceASM() { + handlers.add("org/springframework/web/reactive/DispatcherHandler"); + handlers.add("org/springframework/web/server/handler/FilteringWebHandler"); +// handlersRes.add("org/springframework/http/server/reactive/ReactorServerHttpResponse"); + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (Configure.getInstance()._hook_serivce_enabled == false) { + return cv; + } + if (Configure.getInstance()._hook_reactive_enabled == false) { + return cv; + } + if (handlers.contains(className)) { + return new HttpReactiveServiceCV(cv, className); + } +// if (handlersRes.contains(className)) { +// return new HttpReactiveServiceResCV(cv, className); +// } + return cv; + } +} + +class HttpReactiveServiceCV extends ClassVisitor implements Opcodes { + private static String handler = "invokeHandler"; + private static String handler_sig = "(Lorg/springframework/web/server/ServerWebExchange;Ljava/lang/Object;)Lreactor/core/publisher/Mono;"; + + private static String handler2 = "handle"; + private static String handler_sig2 = "(Lorg/springframework/web/server/ServerWebExchange;)Lreactor/core/publisher/Mono;"; + + private static String loading = ""; + private static String loading_class = "org/springframework/web/reactive/DispatcherHandler"; + + private String className; + + public HttpReactiveServiceCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + + if (desc.startsWith(handler_sig2) && handler2.equals(name) || desc.startsWith(handler_sig) && handler.equals(name)) { + Logger.println("A103", "HTTP-REACTIVE " + className); + return new HttpReactiveServiceMV(access, desc, mv); + + } +// else if (loading.equals(name) && loading_class.equals(className)) { +// Logger.println("A103", "HTTP-REACTIVE INIT" + className); +// return new HttpReactiveInitMV(access, desc, mv); +// } + return mv; + } +} + +//class HttpReactiveInitMV extends LocalVariablesSorter implements Opcodes { +// private static final String TRACEMAIN = TraceMain.class.getName().replace('.', '/'); +// private final static String START = "startReactiveInit"; +// private static final String START_SIGNATURE = "(Ljava/lang/Object;)V"; +// +// public HttpReactiveInitMV(int access, String desc, MethodVisitor mv) { +// super(ASM8, access, desc, mv); +// } +// +// @Override +// public void visitCode() { +// mv.visitVarInsn(ALOAD, 0); +// mv.visitTypeInsn(CHECKCAST, "java/lang/Object"); +// mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACEMAIN, START, START_SIGNATURE, false); +// mv.visitCode(); +// } +//} + +class HttpReactiveServiceMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACEMAIN = TraceMain.class.getName().replace('.', '/'); + private final static String START = "startReactiveHttpService"; + private static final String START_SIGNATURE = "(Ljava/lang/Object;)V"; + private final static String START_RETURN = "startReactiveHttpServiceReturn"; + private final static String START_RETURN_SIGNATUER = "(Ljava/lang/Object;)Ljava/lang/Object;"; + + //TODO private final static String REJECT = "reject"; + + public HttpReactiveServiceMV(int access, String desc, MethodVisitor mv) { + super(ASM8, access, desc, mv); + } + + @Override + public void visitCode() { + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACEMAIN, START, START_SIGNATURE, false); + mv.visitCode(); + } + + @Override + public void visitInsn(int opcode) { + if ((opcode >= IRETURN && opcode <= RETURN)) { + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACEMAIN, START_RETURN, START_RETURN_SIGNATUER, false); + mv.visitTypeInsn(CHECKCAST, "reactor/core/publisher/Mono"); + } + mv.visitInsn(opcode); + } +} + + +//class HttpReactiveServiceResCV extends ClassVisitor implements Opcodes { +// private static String method1 = "writeWithInternal"; +// private static String desc1 = "(Lorg/reactivestreams/Publisher;)Lreactor/core/publisher/Mono;"; +// private static String method2 = "writeAndFlushWithInternal"; +// private static String desc2 = "(Lorg/reactivestreams/Publisher;)Lreactor/core/publisher/Mono;"; +// +// private String className; +// +// public HttpReactiveServiceResCV(ClassVisitor cv, String className) { +// super(ASM8, cv); +// this.className = className; +// } +// @Override +// public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { +// MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); +// if (mv == null) { +// return mv; +// } +// +// if (method1.equals(name) && desc.startsWith(desc1) || method2.equals(name) && desc.startsWith(desc2) ) { +// Logger.println("A103", "HTTP-REACTIVE-RES " + className); +// return new HttpReactiveServiceResMV(access, desc, mv); +// } +// return mv; +// } +//} +// +//class HttpReactiveServiceResMV extends LocalVariablesSorter implements Opcodes { +// private static final String TRACEMAIN = TraceMain.class.getName().replace('.', '/'); +// private final static String METHOD = "endReactiveHttpService"; +// private static final String METHOD_SIGNATURE = "()V"; +// +// +// public HttpReactiveServiceResMV(int access, String desc, MethodVisitor mv) { +// super(ASM8, access, desc, mv); +// } +// +// @Override +// public void visitCode() { +// mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACEMAIN, METHOD, METHOD_SIGNATURE, false); +// mv.visitCode(); +// } +//} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/HttpServiceASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/HttpServiceASM.java index f98406fe3..1dca8d6d7 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/HttpServiceASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/HttpServiceASM.java @@ -52,7 +52,7 @@ class HttpServiceCV extends ClassVisitor implements Opcodes { private static String TARGET_SIGNATURE = "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;"; private String className; public HttpServiceCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @Override @@ -86,7 +86,7 @@ class HttpServiceMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); private boolean httpservlet; public HttpServiceMV(int access, String desc, MethodVisitor mv, boolean httpservlet) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.httpservlet = httpservlet; } private int statIdx; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/InitialContextASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/InitialContextASM.java index 99157a5a8..546be13a0 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/InitialContextASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/InitialContextASM.java @@ -50,7 +50,7 @@ class InitialContextCV extends ClassVisitor implements Opcodes { public String className; public InitialContextCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -78,7 +78,7 @@ class InitialContextMV extends LocalVariablesSorter implements Opcodes { public InitialContextMV(int access, String desc, MethodVisitor mv, String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.returnType = Type.getReturnType(desc); } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCConnectionOpenASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCConnectionOpenASM.java index efd5761d4..13b4b3149 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCConnectionOpenASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCConnectionOpenASM.java @@ -84,7 +84,7 @@ class DbcOpenCV extends ClassVisitor implements Opcodes { private HookingSet mset; public DbcOpenCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -118,7 +118,7 @@ class DbcOpenMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); public DbcOpenMV(int access, String desc, MethodVisitor mv, String fullname, int fullname_hash) { - super(Opcodes.ASM7, access, desc, mv); + super(Opcodes.ASM8, access, desc, mv); this.fullname = fullname; this.fullname_hash = fullname_hash; this.isStatic = (access & ACC_STATIC) != 0; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCDriverASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCDriverASM.java index 24be8498a..75d896637 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCDriverASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCDriverASM.java @@ -49,7 +49,7 @@ class JDBCDriverCV extends ClassVisitor implements Opcodes { public String className; private HookingSet mset; public JDBCDriverCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -78,7 +78,7 @@ class JDBCDriverMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); public JDBCDriverMV(int access, String desc, MethodVisitor mv, String fullname) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); this.strArgIdx = AsmUtil.getStringIdx(access, desc); } private int strArgIdx; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCGetConnectionASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCGetConnectionASM.java index 02aad4940..a1d919a55 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCGetConnectionASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCGetConnectionASM.java @@ -67,7 +67,7 @@ class DataSourceCV extends ClassVisitor implements Opcodes { private HookingSet mset; public DataSourceCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; @@ -98,7 +98,7 @@ class DataSourceMV extends LocalVariablesSorter implements Opcodes { private String methodDesc; public DataSourceMV(int access, String desc, MethodVisitor mv, String className, String methodName) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); this.returnType = Type.getReturnType(desc); this.className = className; this.methodName = methodName; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCPreparedStatementASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCPreparedStatementASM.java index 0ec1199db..8b7807d76 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCPreparedStatementASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCPreparedStatementASM.java @@ -111,7 +111,7 @@ class PreparedStatementCV extends ClassVisitor implements Opcodes { private String owner; public PreparedStatementCV(ClassVisitor cv, HashSet noField) { - super(ASM7, cv); + super(ASM8, cv); this.noField = noField; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCResultSetASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCResultSetASM.java index 9203bc26b..b08bea4b3 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCResultSetASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCResultSetASM.java @@ -119,7 +119,7 @@ class ResultSetCV extends ClassVisitor implements Opcodes { JDBCResultSetASM.Scope scope; public ResultSetCV(ClassVisitor cv, JDBCResultSetASM.Scope scope) { - super(ASM7, cv); + super(ASM8, cv); this.scope = scope; } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCStatementASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCStatementASM.java index bde39f9ad..c0a2c1623 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCStatementASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JDBCStatementASM.java @@ -76,7 +76,7 @@ public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc class class StatementCV extends ClassVisitor implements Opcodes { private String owner; public StatementCV(ClassVisitor cv) { - super(ASM7, cv); + super(ASM8, cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/JspServletASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/JspServletASM.java index 88fda5f4c..0b5bb3c06 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/JspServletASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/JspServletASM.java @@ -72,7 +72,7 @@ class JspServletCV extends ClassVisitor implements Opcodes { private HookingSet mset; public JspServletCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -100,7 +100,7 @@ class JspServletMV extends LocalVariablesSorter implements Opcodes { private boolean isStatic; public JspServletMV(int access, String desc, MethodVisitor mv, Type[] paramTypes, boolean isStatic) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.paramTypes = paramTypes; this.isStatic = isStatic; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/MapImplASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/MapImplASM.java index 2245801fe..8619bf486 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/MapImplASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/MapImplASM.java @@ -33,7 +33,7 @@ class MapImplCV extends ClassVisitor implements Opcodes { private String className; public MapImplCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @Override @@ -58,7 +58,7 @@ class MapImplMV extends LocalVariablesSorter implements Opcodes { private String className; public MapImplMV(int access, String desc, MethodVisitor mv, String className) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.className = className; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/MethodASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/MethodASM.java index 901c3a86e..52a8e84ae 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/MethodASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/MethodASM.java @@ -91,7 +91,7 @@ class MethodCV extends ClassVisitor implements Opcodes { private List excludeTarget; public MethodCV(ClassVisitor cv, HookingSet mset, List excludeTarget, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.excludeTarget = excludeTarget; this.className = className; @@ -168,7 +168,7 @@ class MethodMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); public MethodMV(int access, String desc, MethodVisitor mv, String fullname, int fullname_hash) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.fullname = fullname; this.fullname_hash = fullname_hash; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/ServiceASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/ServiceASM.java index de41a9a02..bd7aef2ca 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/ServiceASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/ServiceASM.java @@ -74,7 +74,7 @@ class ServiceCV extends ClassVisitor implements Opcodes { private byte xType; public ServiceCV(ClassVisitor cv, HookingSet mset, String className,byte xType) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; this.xType=xType; @@ -124,7 +124,7 @@ class ServiceMV extends LocalVariablesSorter implements Opcodes { public ServiceMV(int access, String desc, MethodVisitor mv, String fullname,Type[] paramTypes, boolean isStatic,byte xType,String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.fullname = fullname; this.paramTypes = paramTypes; this.strArgIdx = AsmUtil.getStringIdx(access, desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/SocketASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/SocketASM.java index 5b194c454..544ba8194 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/SocketASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/SocketASM.java @@ -55,7 +55,7 @@ class SocketCV extends ClassVisitor implements Opcodes { private HookingSet mset; public SocketCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -84,7 +84,7 @@ class SocketMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); public SocketMV(int access, String desc, MethodVisitor mv) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/SpringReqMapASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/SpringReqMapASM.java index cd6a7eb98..090374fd3 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/SpringReqMapASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/SpringReqMapASM.java @@ -68,7 +68,7 @@ class SpringReqMapCV extends ClassVisitor implements Opcodes { public String classRequestMappingUrl; public SpringReqMapCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -97,7 +97,7 @@ public MethodVisitor visitMethod(int access, String methodName, String desc, Str class SpringReqMapCVAV extends AnnotationVisitor implements Opcodes { public SpringReqMapCVAV(AnnotationVisitor av) { - super(ASM7, av); + super(ASM8, av); } @Override @@ -114,7 +114,7 @@ public AnnotationVisitor visitArray(String name) { class SpringReqMapCVAVAV extends AnnotationVisitor implements Opcodes { public SpringReqMapCVAVAV(AnnotationVisitor av) { - super(ASM7, av); + super(ASM8, av); } @Override @@ -145,7 +145,7 @@ class SpringReqMapMV extends LocalVariablesSorter implements Opcodes { private String desc; public SpringReqMapMV(String className, int access, String methodName, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.className = className; this.access = access; this.methodName = methodName; @@ -266,7 +266,7 @@ public void visitCode() { class SpringReqMapMVAV extends AnnotationVisitor implements Opcodes { public SpringReqMapMVAV(AnnotationVisitor av) { - super(ASM7, av); + super(ASM8, av); } @Override @@ -285,7 +285,7 @@ class SpringReqMapMVAVAV extends AnnotationVisitor implements Opcodes { String paramName; public SpringReqMapMVAVAV(AnnotationVisitor av, String paramName) { - super(ASM7, av); + super(ASM8, av); this.paramName = paramName; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/SqlMapASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/SqlMapASM.java index 65e05651e..2a1f31172 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/SqlMapASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/SqlMapASM.java @@ -74,7 +74,7 @@ class SqlMapCV extends ClassVisitor implements Opcodes { public String className; public SqlMapCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -106,7 +106,7 @@ class SqlMapMV extends LocalVariablesSorter implements Opcodes { public SqlMapMV(int access, String desc, MethodVisitor mv, Type[] paramTypes, boolean isStatic, String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.paramTypes = paramTypes; this.isStatic = isStatic; this.methodName = methodname; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionASM.java index cdc06b95c..9052da3bb 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionASM.java @@ -58,7 +58,7 @@ class UserExceptionCV extends ClassVisitor implements Opcodes { private String className; public UserExceptionCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -83,7 +83,7 @@ class UserExceptionConsturtorMV extends MethodVisitor implements Opcodes { private String methodDesc; public UserExceptionConsturtorMV(String classname, String methoddesc, MethodVisitor mv) { - super(ASM7, mv); + super(ASM8, mv); this.className = classname; this.methodDesc = methoddesc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionHandlerASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionHandlerASM.java index 6e4705400..f51f51089 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionHandlerASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/UserExceptionHandlerASM.java @@ -53,7 +53,7 @@ class UserExceptionHandlerCV extends ClassVisitor implements Opcodes { private HookingSet mset; public UserExceptionHandlerCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -87,7 +87,7 @@ class UserExceptionHandlerMV extends LocalVariablesSorter implements Opcodes { public UserExceptionHandlerMV(int access, String desc, MethodVisitor mv, Type[] paramTypes, boolean isStatic, String classname, String methodname, String methoddesc) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.paramTypes = paramTypes; this.isStatic = isStatic; this.className = classname; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/UserTxASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/UserTxASM.java index 353a9fa06..a9b1cb517 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/UserTxASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/UserTxASM.java @@ -48,7 +48,7 @@ public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc class class UserTxCV extends ClassVisitor implements Opcodes { public UserTxCV(ClassVisitor cv) { - super(ASM7, cv); + super(ASM8, cv); } @Override public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) { @@ -72,7 +72,7 @@ class UTXOpenMV extends LocalVariablesSorter implements Opcodes { private static final String SIGNATURE = "()V"; public UTXOpenMV(int access, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); } @Override @@ -91,7 +91,7 @@ class UTXCloseMV extends LocalVariablesSorter implements Opcodes { private String method; public UTXCloseMV(int access, String desc, MethodVisitor mv, String method) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.method = method; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/AsyncContextDispatchASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/AsyncContextDispatchASM.java index 8dcf61f3b..62792d498 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/AsyncContextDispatchASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/AsyncContextDispatchASM.java @@ -70,7 +70,7 @@ class AsyncContextCV extends ClassVisitor implements Opcodes { HookingSet mset; public AsyncContextCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -104,7 +104,7 @@ class DispatchMV extends LocalVariablesSorter implements Opcodes { String desc; public DispatchMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CallRunnableASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CallRunnableASM.java index 4ac29d076..a12cdf98c 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CallRunnableASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CallRunnableASM.java @@ -94,7 +94,7 @@ class CallRunnableCV extends ClassVisitor implements Opcodes { String className; public CallRunnableCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -136,7 +136,7 @@ class CallOrRunMV extends LocalVariablesSorter implements Opcodes { private int statIdx; public CallOrRunMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); @@ -217,7 +217,7 @@ class InitMV extends LocalVariablesSorter implements Opcodes { String desc; public InitMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CoroutineThreadNameASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CoroutineThreadNameASM.java new file mode 100644 index 000000000..6397019c0 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/CoroutineThreadNameASM.java @@ -0,0 +1,133 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.asyncsupport; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.IASM; +import scouter.agent.trace.TraceReactive; + +public class CoroutineThreadNameASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public CoroutineThreadNameASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf._hook_coroutine_debugger_hook_enabled == false) { + return cv; + } + + if ("kotlinx/coroutines/CoroutineId".equals(className)) { + return new CoroutineIdCV(cv, className); + } + return cv; + } +} + +class CoroutineIdCV extends ClassVisitor implements Opcodes { + + public String className; + + public CoroutineIdCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + + if ("updateThreadContext".equals(name) && "(Lkotlin/coroutines/CoroutineContext;)Ljava/lang/String;".equals(desc)) { + return new CoroutineIdUpdateThreadContextMV(access, desc, mv, className); + + } else if ("restoreThreadContext".equals(name) && "(Lkotlin/coroutines/CoroutineContext;Ljava/lang/String;)V".equals(desc)) { + return new CoroutineIdRestoreThreadContextMV(access, desc, mv, className); + } + return mv; + } +} + +class CoroutineIdUpdateThreadContextMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceReactive.class.getName().replace('.', '/'); + + private Label startFinally = new Label(); + private String className; + + public CoroutineIdUpdateThreadContextMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + + mv.visitFieldInsn(GETFIELD, className, "id", "J"); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, "startCoroutineIdUpdateThreadContext", "(J)V", false); + mv.visitLabel(startFinally); + mv.visitCode(); + } + + @Override + public void visitInsn(int opcode) { + if ((opcode >= IRETURN && opcode <= RETURN)) { + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, "endCoroutineIdUpdateThreadContext", "()V", false); + } + mv.visitInsn(opcode); + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + Label endFinally = new Label(); + mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null); + mv.visitLabel(endFinally); + + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, "endCoroutineIdUpdateThreadContext", "()V", false); + mv.visitInsn(ATHROW); + mv.visitMaxs(maxStack + 8, maxLocals + 2); + } +} + + +class CoroutineIdRestoreThreadContextMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceReactive.class.getName().replace('.', '/'); + + private String className; + + public CoroutineIdRestoreThreadContextMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, "startCoroutineIdRestoreThreadContext", "(Ljava/lang/Object;)V", false); + mv.visitCode(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/HystrixCommandASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/HystrixCommandASM.java index d4ca0d249..a3a2ffad5 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/HystrixCommandASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/HystrixCommandASM.java @@ -74,7 +74,7 @@ class HystrixCommandPrepareCV extends ClassVisitor implements Opcodes { private HookingSet mset; public HystrixCommandPrepareCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -97,7 +97,7 @@ class HystrixCommandReceiveCV extends ClassVisitor implements Opcodes { private HookingSet mset; public HystrixCommandReceiveCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -134,7 +134,7 @@ class HystrixCommandReceiveMV extends LocalVariablesSorter implements Opcodes { private int statIdx; public HystrixCommandReceiveMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); @@ -215,7 +215,7 @@ class HystrixCommandPrepareMV extends LocalVariablesSorter implements Opcodes { String desc; public HystrixCommandPrepareMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/LambdaFormASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/LambdaFormASM.java index aee42aa94..d770a72dc 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/LambdaFormASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/LambdaFormASM.java @@ -47,7 +47,7 @@ class LambdaFormCV extends ClassVisitor implements Opcodes { String factoryMethodDesc; public LambdaFormCV(ClassVisitor cv, String className, String lambdaMethodName, String lambdaMethodDesc, String factoryMethodName, String factoryMethodDesc) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; this.lambdaMethodName = lambdaMethodName; this.lambdaMethodDesc = lambdaMethodDesc; @@ -107,7 +107,7 @@ class LambdaMV extends LocalVariablesSorter implements Opcodes { public LambdaMV(int access, String name, String desc, MethodVisitor mv, String fullName, Type[] paramTypes, String className) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; @@ -217,7 +217,7 @@ class FacotoryMV extends LocalVariablesSorter implements Opcodes { private Type returnType; public FacotoryMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/MonoKtASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/MonoKtASM.java new file mode 100644 index 000000000..76f1cd776 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/MonoKtASM.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.asyncsupport; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.IASM; +import scouter.agent.trace.TraceReactive; + +public class MonoKtASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public MonoKtASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf._hook_coroutine_enabled == false) { + return cv; + } + if (conf._hook_reactive_enabled == false) { + return cv; + } + + if ("kotlinx/coroutines/reactor/MonoKt".equals(className)) { + return new MonoKtCV(cv, className); + } + return cv; + } +} + +class MonoKtCV extends ClassVisitor implements Opcodes { + + public String className; + + public MonoKtCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + + if ("mono".equals(name) && "(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Mono;".equals(desc)) { + return new MonoKtMV(access, desc, mv, className); + + } + return mv; + } +} + +class MonoKtMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceReactive.class.getName().replace('.', '/'); + + private Label startFinally = new Label(); + private String className; + + public MonoKtMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, "startMonoKtMono", "(Ljava/lang/Object;)Ljava/lang/Object;", false); + mv.visitVarInsn(ASTORE, 0); + mv.visitLabel(startFinally); + mv.visitCode(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/RequestStartAsyncASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/RequestStartAsyncASM.java index 5877304a8..1afec8bed 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/RequestStartAsyncASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/RequestStartAsyncASM.java @@ -68,7 +68,7 @@ class RequestCV extends ClassVisitor implements Opcodes { HookingSet mset; public RequestCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -96,7 +96,7 @@ class StartAsyncMV extends LocalVariablesSorter implements Opcodes { private Type returnType; public StartAsyncMV(int access, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); returnType = Type.getReturnType(desc); } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/ThreadASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/ThreadASM.java new file mode 100644 index 000000000..350e176d7 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/ThreadASM.java @@ -0,0 +1,89 @@ +package scouter.agent.asm.asyncsupport; +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.asm.IASM; +import scouter.agent.trace.TraceReactive; + +/** + * Created by Gun Lee(gunlee01@gmail.com) on 30/07/2020 + */ +public class ThreadASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + @Override + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf._hook_thread_name_enabled == false) { + return cv; + } + + if ("java/lang/Thread".equals(className)){ + return new ThreadCV(cv, className); + } + + return cv; + } +} + +class ThreadCV extends ClassVisitor implements Opcodes { + + private String className; + public ThreadCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + Logger.println("G001", "Thread.class - " + className); + } + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + if ("setName".equals(name)) { + return new ThreadNameMV(access, desc, mv, className); + } + return mv; + } +} + +class ThreadNameMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceReactive.class.getName().replace('.', '/'); + + private String className; + + public ThreadNameMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, "threadSetName", "(Ljava/lang/Thread;Ljava/lang/String;)V", false); + + mv.visitCode(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/executor/ExecutorServiceASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/executor/ExecutorServiceASM.java index 1f4d150c1..9e70ecea3 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/executor/ExecutorServiceASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/executor/ExecutorServiceASM.java @@ -40,12 +40,9 @@ public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc class if (conf.hook_async_thread_pool_executor_enabled == false) { return cv; } - Logger.trace("[SCTRACE]className IN ExecutorServiceASM : " + className); if (THREAD_POOL_EXECUTOR_CLASS_NAME.equals(className)) { - Logger.trace("[SCTRACE]transform ThreadPoolExecutor"); return new ThreadPoolExecutorCV(cv, className); } else if (ABSTRACT_EXECUTOR_SERVICE_CLASS_NAME.equals(className)) { - Logger.trace("[SCTRACE]transform AbstractExecutorService"); return new AbstractExecutorServiceCV(cv, className); } @@ -57,7 +54,7 @@ class ThreadPoolExecutorCV extends ClassVisitor implements Opcodes { String className; public ThreadPoolExecutorCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -87,7 +84,7 @@ class ThreadPoolExecutorExecuteMV extends LocalVariablesSorter implements Opcode String desc; public ThreadPoolExecutorExecuteMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } @@ -105,7 +102,7 @@ class AbstractExecutorServiceCV extends ClassVisitor implements Opcodes { String className; public AbstractExecutorServiceCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -129,7 +126,7 @@ class AbstraceExecutorServiceSubmitMV extends LocalVariablesSorter implements Op String desc; public AbstraceExecutorServiceSubmitMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } @@ -152,7 +149,7 @@ class ThreadPoolExecutorGetTaskMV extends LocalVariablesSorter implements Opcode String desc; public ThreadPoolExecutorGetTaskMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionASM.java index fff54aa30..d9ebc7209 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionASM.java @@ -58,7 +58,7 @@ class SpringAsyncExecutionCV extends ClassVisitor implements Opcodes { HookingSet mset; public SpringAsyncExecutionCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -89,7 +89,7 @@ class SubmitMV extends LocalVariablesSorter implements Opcodes { String desc; public SubmitMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } @@ -112,7 +112,7 @@ class DetermineMV extends LocalVariablesSorter implements Opcodes { String desc; public DetermineMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionAspectSupportDoSubmitASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionAspectSupportDoSubmitASM.java index c577ddf9e..8298848ff 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionAspectSupportDoSubmitASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/asyncsupport/spring/SpringAsyncExecutionAspectSupportDoSubmitASM.java @@ -69,7 +69,7 @@ class SpringAsyncExecutionAspectSupportCV extends ClassVisitor implements Opcode HookingSet mset; public SpringAsyncExecutionAspectSupportCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -95,7 +95,7 @@ class DoSubmitMV extends LocalVariablesSorter implements Opcodes { private static final String CALL_SIGNATURE = "(Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable;"; public DoSubmitMV(int access, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/elasticsearch/HttpNioEntityASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/elasticsearch/HttpNioEntityASM.java new file mode 100644 index 000000000..ddd335aee --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/elasticsearch/HttpNioEntityASM.java @@ -0,0 +1,111 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.elasticsearch; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.asm.IASM; + +import static scouter.agent.AgentCommonConstant.SCOUTER_ADDED_FIELD; + +public class HttpNioEntityASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public HttpNioEntityASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf._hook_elasticsearch_enabled == false) { + return cv; + } + + if ("org/apache/http/nio/entity/NByteArrayEntity".equals(className) + || "org/apache/http/nio/entity/NStringEntity".equals(className)) { + return new HttpNioEntityCV(cv, className); + } + return cv; + } +} + +class HttpNioEntityCV extends ClassVisitor implements Opcodes { + + public String className; + + public HttpNioEntityCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + + boolean exist = false; + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + if (!exist) { + super.visitField(ACC_PUBLIC, SCOUTER_ADDED_FIELD, Type.getDescriptor(Object.class), null, null).visitEnd(); + } + } + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + if (name.equals(SCOUTER_ADDED_FIELD)) { + exist = true; + Logger.println("A901e", "fail to add the field " + name + " on " + className); + } + return super.visitField(access, name, desc, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + + if ("".equals(name) && + (desc.startsWith("([B") || desc.startsWith("(Ljava/lang/String;"))) { + return new HttpNioEntityMV(access, desc, mv, className); + } + return mv; + } +} + +class HttpNioEntityMV extends LocalVariablesSorter implements Opcodes { + private String className; + + public HttpNioEntityMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitInsn(int opcode) { + if ((opcode >= IRETURN && opcode <= RETURN)) { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, className, SCOUTER_ADDED_FIELD, "Ljava/lang/Object;"); + } + mv.visitInsn(opcode); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/elasticsearch/RestClientASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/elasticsearch/RestClientASM.java new file mode 100644 index 000000000..0fc2962cf --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/elasticsearch/RestClientASM.java @@ -0,0 +1,161 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.elasticsearch; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.IASM; +import scouter.agent.trace.TraceElasticSearch; + +public class RestClientASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public RestClientASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf._hook_elasticsearch_enabled == false) { + return cv; + } + + if ("org/elasticsearch/client/RestClient".equals(className)) { + return new RestClientCV(cv, className); + } else if ("org/elasticsearch/client/RequestLogger".equals(className)) { + return new RequestLoggerCV(cv, className); + } + return cv; + } + + static class RestClientCV extends ClassVisitor implements Opcodes { + public String className; + + public RestClientCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + + if ("performRequestAsync".equals(name) && desc.startsWith("(JLorg/elasticsearch/client/RestClient$NodeTuple;Lorg/apache/http/client/methods/HttpRequestBase;")) { + return new RestClientStartMV(access, desc, mv, className); + } + return mv; + } + } + + static class RestClientStartMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceElasticSearch.class.getName().replace('.', '/'); + private final static String METHOD = "startRequest"; + private static final String SIGNATURE = "(Ljava/lang/Object;)V"; + + private String className; + + public RestClientStartMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 4); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, METHOD, SIGNATURE, false); + mv.visitCode(); + } + } + + + static class RequestLoggerCV extends ClassVisitor implements Opcodes { + public String className; + + public RequestLoggerCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + if ("logResponse".equals(name) + && desc.startsWith("(Lorg/apache/commons/logging/Log;Lorg/apache/http/client/methods/HttpUriRequest;Lorg/apache/http/HttpHost;Lorg/apache/http/HttpResponse;")) { + return new RequestLoggerMV(access, desc, mv, className); + + } else if ("logFailedRequest".equals(name) + && desc.startsWith("(Lorg/apache/commons/logging/Log;Lorg/apache/http/client/methods/HttpUriRequest;Lorg/elasticsearch/client/Node;Ljava/lang/Exception;")) { + return new RequestFailLoggerMV(access, desc, mv, className); + } + return mv; + } + + static class RequestLoggerMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceElasticSearch.class.getName().replace('.', '/'); + private final static String METHOD = "endRequest"; + private static final String SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V"; + + private String className; + + public RequestLoggerMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 1); //HttpUriRequest + mv.visitVarInsn(ALOAD, 2); //HttpHost + mv.visitVarInsn(ALOAD, 3); //HttpResponse + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, METHOD, SIGNATURE, false); + mv.visitCode(); + } + } + + static class RequestFailLoggerMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceElasticSearch.class.getName().replace('.', '/'); + private final static String METHOD = "endFailRequest"; + private static final String SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Exception;)V"; + + private String className; + + public RequestFailLoggerMV(int access, String desc, MethodVisitor mv, String className) { + super(ASM8, access, desc, mv); + this.className = className; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 1); //HttpUriRequest + mv.visitVarInsn(ALOAD, 2); //Node + mv.visitVarInsn(ALOAD, 3); //Exception + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, METHOD, SIGNATURE, false); + mv.visitCode(); + } + } + } +} \ No newline at end of file diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsClearParametersMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsClearParametersMV.java index 747473bb9..5e0d5bbed 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsClearParametersMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsClearParametersMV.java @@ -28,7 +28,7 @@ public class PsClearParametersMV extends LocalVariablesSorter implements Opcodes // ///////////////////////////////////////////////////////////////// public PsClearParametersMV(int access, String desc, MethodVisitor mv, String owner) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); this.owner = owner; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsCloseMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsCloseMV.java index bf534a3db..e9c060345 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsCloseMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsCloseMV.java @@ -26,7 +26,7 @@ public class PsCloseMV extends MethodVisitor implements Opcodes { private static final String SIGNATURE = "(Ljava/lang/Object;)V"; public PsCloseMV(MethodVisitor mv) { - super(ASM7, mv); + super(ASM8, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsExecuteMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsExecuteMV.java index a17253a1d..753eef632 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsExecuteMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsExecuteMV.java @@ -58,7 +58,7 @@ public static boolean isTarget(String name, String desc) { private static final String END_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Throwable;I)V"; public PsExecuteMV(int access, String desc, MethodVisitor mv, String owner,String name) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); this.owner = owner; this.returnType = Type.getReturnType(desc); this.desc = desc; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsInitMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsInitMV.java index b663e7ae2..08b3f0884 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsInitMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsInitMV.java @@ -45,7 +45,7 @@ public class PsInitMV extends LocalVariablesSorter implements Opcodes { private boolean isUstatement = false; public PsInitMV(int access, String desc, MethodVisitor mv, String owner) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); this.owner = owner; this.sqlIdx = AsmUtil.getStringIdx(access, desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsSetMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsSetMV.java index 198020773..c07a1c469 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsSetMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsSetMV.java @@ -62,7 +62,7 @@ public static String getSetSignature(String name) { private final static String TRACESQL = TraceSQL.class.getName().replace('.', '/'); public PsSetMV(int access, String name, String desc, MethodVisitor mv, String owner) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); this.owner = owner; this.args = Type.getArgumentTypes(desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsUpdateCountMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsUpdateCountMV.java index 21d656b21..e36b3a617 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsUpdateCountMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/PsUpdateCountMV.java @@ -32,7 +32,7 @@ public class PsUpdateCountMV extends MethodVisitor implements Opcodes { private static final String SIGNATURE = "(I)I"; public PsUpdateCountMV(MethodVisitor mv) { - super(ASM7, mv); + super(ASM8, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsCloseMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsCloseMV.java index 22a6ad47b..afc8cae5d 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsCloseMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsCloseMV.java @@ -26,7 +26,7 @@ public class RsCloseMV extends MethodVisitor implements Opcodes { private static final String SIGNATURE = "(Ljava/lang/Object;)V"; public RsCloseMV(MethodVisitor mv) { - super(ASM7, mv); + super(ASM8, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsInitMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsInitMV.java index dc5ede367..e8fc67519 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsInitMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsInitMV.java @@ -32,7 +32,7 @@ public class RsInitMV extends LocalVariablesSorter implements Opcodes { private final static String SIGNATURE = "(Ljava/lang/Object;)V"; public RsInitMV(int access, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsNextMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsNextMV.java index 661105abc..92348600b 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsNextMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/RsNextMV.java @@ -27,7 +27,7 @@ public class RsNextMV extends MethodVisitor implements Opcodes { private static final String SIGNATURE = "(Z)Z"; public RsNextMV(MethodVisitor mv) { - super(ASM7, mv); + super(ASM8, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StExecuteMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StExecuteMV.java index db8b3b221..8c3716087 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StExecuteMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StExecuteMV.java @@ -51,7 +51,7 @@ public static boolean isTarget(String name) { private static final String END_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Throwable;I)V"; public StExecuteMV(int access, String desc, MethodVisitor mv, String owner, String name) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.returnType = Type.getReturnType(desc); this.desc = desc; this.methodType = methodType(name); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StInitMV.java b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StInitMV.java index 4a2431505..60142a7f2 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StInitMV.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/jdbc/StInitMV.java @@ -28,7 +28,7 @@ public class StInitMV extends LocalVariablesSorter implements Opcodes { private final static String SIGNATURE_INIT = "(Ljava/lang/Object;)V"; public StInitMV(int access, String desc, MethodVisitor mv) { - super(ASM7,access, desc, mv); + super(ASM8,access, desc, mv); } @Override diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/mongodb/MongoCommandProtocolASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/mongodb/MongoCommandProtocolASM.java new file mode 100644 index 000000000..d7248a7c8 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/mongodb/MongoCommandProtocolASM.java @@ -0,0 +1,295 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.mongodb; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.LocalVariablesSorter; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.IASM; +import scouter.agent.trace.TraceMongoDB; + +import static scouter.agent.trace.TraceMongoDB.V364; +import static scouter.agent.trace.TraceMongoDB.V382; +import static scouter.agent.trace.TraceMongoDB.V405; + +public class MongoCommandProtocolASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public MongoCommandProtocolASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf.hook_mongodb_enabled == false) { + return cv; + } + + if ("com/mongodb/internal/connection/CommandProtocolImpl".equals(className) || + "com/mongodb/connection/CommandProtocolImpl".equals(className) + ) { + return new MongoCommandProtocolCV(cv, className); + } + return cv; + } + + static class MongoCommandProtocolCV extends ClassVisitor implements Opcodes { + public String className; + + public MongoCommandProtocolCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + if (name.equals("namespace") && descriptor.equals("Lcom/mongodb/MongoNamespace;")) { + namespace = true; + } else if (name.equals("command") && descriptor.equals("Lorg/bson/BsonDocument;")) { + command = true; + } else if (name.equals("readPreference") && descriptor.equals("Lcom/mongodb/ReadPreference;")) { + readPreference = true; + } else if (name.equals("payload") + && (descriptor.equals("Lcom/mongodb/internal/connection/SplittablePayload;") + || descriptor.equals("Lcom/mongodb/connection/SplittablePayload;"))) { + payload = true; + + if (descriptor.equals("Lcom/mongodb/connection/SplittablePayload;")) { + version = V382; + } + } + return super.visitField(newAccess, name, descriptor, signature, value); + } + + boolean namespace; + boolean command; + boolean readPreference; + boolean payload; + String version = V405; + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (mv == null) { + return mv; + } + + if ("executeAsync".equals(name) && desc.startsWith("(Lcom/mongodb/internal/connection/InternalConnection;Lcom/mongodb/internal/async/SingleResultCallback;)V")) { + return new ExecuteAsyncMV(access, desc, mv, className, namespace, command, readPreference, payload, V405); + + } else if ("executeAsync".equals(name) && desc.startsWith("(Lcom/mongodb/internal/connection/InternalConnection;Lcom/mongodb/async/SingleResultCallback;)V")) { + return new ExecuteAsyncMV(access, desc, mv, className, namespace, command, readPreference, payload, V382); + + } else if ("execute".equals(name) && desc.startsWith("(Lcom/mongodb/internal/connection/InternalConnection;)")) { + return new ExecuteMV(access, desc, mv, className, namespace, command, readPreference, payload, version); + + } else if ("execute".equals(name) && desc.startsWith("(Lcom/mongodb/connection/InternalConnection;)")) { + return new ExecuteMV(access, desc, mv, className, namespace, command, readPreference, payload, V364); + } + return mv; + } + } + + static class ExecuteAsyncMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceMongoDB.class.getName().replace('.', '/'); + private final static String METHOD = "startExecuteAsync"; + private static final String SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;" + + "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"; + + private String className; + + boolean namespace; + boolean command; + boolean readPreference; + boolean payload; + String version; + + public ExecuteAsyncMV(int access, String desc, MethodVisitor mv, String className, boolean namespace, + boolean command, boolean readPreference, boolean payload, String version) { + super(ASM8, access, desc, mv); + this.className = className; + this.namespace = namespace; + this.command = command; + this.readPreference = readPreference; + this.payload = payload; + this.version = version; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + if (namespace) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "namespace", "Lcom/mongodb/MongoNamespace;"); + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + if (command) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "command", "Lorg/bson/BsonDocument;"); + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + if (readPreference) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "readPreference", "Lcom/mongodb/ReadPreference;"); + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + if (payload) { + mv.visitVarInsn(ALOAD, 0); + if (version.equals(V405)) { + mv.visitFieldInsn(GETFIELD, className, "payload", "Lcom/mongodb/internal/connection/SplittablePayload;"); + } else { + mv.visitFieldInsn(GETFIELD, className, "payload", "Lcom/mongodb/connection/SplittablePayload;"); + } + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn(version); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, METHOD, SIGNATURE, false); + if (version.equals(V405)) { + mv.visitTypeInsn(CHECKCAST, "com/mongodb/internal/async/SingleResultCallback"); + } else { + mv.visitTypeInsn(CHECKCAST, "com/mongodb/async/SingleResultCallback"); + } + mv.visitVarInsn(ASTORE, 2); + mv.visitCode(); + } + } + + static class ExecuteMV extends LocalVariablesSorter implements Opcodes { + private static final String TRACE = TraceMongoDB.class.getName().replace('.', '/'); + private final static String METHOD = "startExecute"; + private static final String SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;" + + "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"; + + private final static String END_METHOD = "endExecute"; + private final static String END_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Throwable;)V";; + + private String className; + + boolean namespace; + boolean command; + boolean readPreference; + boolean payload; + String version; + + private int statIdx; + private Label startFinally = new Label(); + + public ExecuteMV(int access, String desc, MethodVisitor mv, String className, boolean namespace, + boolean command, boolean readPreference, boolean payload, String version) { + super(ASM8, access, desc, mv); + this.className = className; + this.namespace = namespace; + this.command = command; + this.readPreference = readPreference; + this.payload = payload; + this.version = version; + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + if (namespace) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "namespace", "Lcom/mongodb/MongoNamespace;"); + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + if (command) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "command", "Lorg/bson/BsonDocument;"); + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + if (readPreference) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "readPreference", "Lcom/mongodb/ReadPreference;"); + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + if (payload) { + mv.visitVarInsn(ALOAD, 0); + if (version.equals(V405)) { + mv.visitFieldInsn(GETFIELD, className, "payload", "Lcom/mongodb/internal/connection/SplittablePayload;"); + } else { + mv.visitFieldInsn(GETFIELD, className, "payload", "Lcom/mongodb/connection/SplittablePayload;"); + } + } else { + mv.visitInsn(Opcodes.ACONST_NULL); + } + mv.visitLdcInsn(version); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, METHOD, SIGNATURE, false); + + statIdx = newLocal(Type.getType(Object.class)); + mv.visitVarInsn(Opcodes.ASTORE, statIdx); + mv.visitLabel(startFinally); + + mv.visitCode(); + } + + @Override + public void visitInsn(int opcode) { + if ((opcode >= IRETURN && opcode <= RETURN)) { + mv.visitVarInsn(Opcodes.ALOAD, statIdx); + mv.visitInsn(Opcodes.ACONST_NULL); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, END_METHOD, END_SIGNATURE, false); + } + mv.visitInsn(opcode); + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + Label endFinally = new Label(); + mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null); + mv.visitLabel(endFinally); + mv.visitInsn(DUP); + int errIdx = newLocal(Type.getType(Throwable.class)); + mv.visitVarInsn(Opcodes.ASTORE, errIdx); + + mv.visitVarInsn(Opcodes.ALOAD, statIdx); + mv.visitVarInsn(Opcodes.ALOAD, errIdx); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, TRACE, END_METHOD, END_SIGNATURE, false); + mv.visitInsn(ATHROW); + mv.visitMaxs(maxStack + 8, maxLocals + 2); + } + } +} \ No newline at end of file diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisCommandASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisCommandASM.java index 542900be1..46f58fdb8 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisCommandASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisCommandASM.java @@ -60,7 +60,7 @@ class JedisCommandCV extends ClassVisitor implements Opcodes { String className; public JedisCommandCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -93,7 +93,7 @@ class JedisCommandMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); public JedisCommandMV(String className, int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.className = className; this.name = name; this.desc = desc; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisProtocolASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisProtocolASM.java index 7df2ca989..895dd4517 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisProtocolASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/JedisProtocolASM.java @@ -70,7 +70,7 @@ class JedisProtocolCV extends ClassVisitor implements Opcodes { HookingSet mset; public JedisProtocolCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -104,7 +104,7 @@ class SendCommandMV extends LocalVariablesSorter implements Opcodes { private Label startFinally = new Label(); public SendCommandMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/LettuceASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/LettuceASM.java index 186e245b3..7e7f97aee 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/LettuceASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/LettuceASM.java @@ -40,7 +40,7 @@ class LettuceCV extends ClassVisitor implements Opcodes { private boolean hasChannelDescriptor = false; LettuceCV(ClassVisitor cv, String className) { - super(ASM7, cv); + super(ASM8, cv); this.className = className; } @@ -80,7 +80,7 @@ class LettuceMV extends LocalVariablesSorter implements Opcodes { private String ownerClass; LettuceMV(int access, String desc, String ownerClass, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.ownerClass = ownerClass; } diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisCacheKeyASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisCacheKeyASM.java index 90667a665..769e3ce0d 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisCacheKeyASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisCacheKeyASM.java @@ -78,7 +78,7 @@ class RedisCacheKeyCV extends ClassVisitor implements Opcodes { boolean existKeyElementField; public RedisCacheKeyCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -123,7 +123,7 @@ class GetKeyBytesMV extends LocalVariablesSorter implements Opcodes { private Type returnType; public GetKeyBytesMV(int access, String className, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.className = className; this.name = name; this.desc = desc; diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisKeyASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisKeyASM.java index d4d25dc1f..67128b288 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisKeyASM.java +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/redis/RedisKeyASM.java @@ -69,7 +69,7 @@ class RedisKeySetCV extends ClassVisitor implements Opcodes { HookingSet mset; public RedisKeySetCV(ClassVisitor cv, HookingSet mset, String className) { - super(ASM7, cv); + super(ASM8, cv); this.mset = mset; this.className = className; } @@ -98,7 +98,7 @@ class KeySetMV extends LocalVariablesSorter implements Opcodes { private Type returnType; public KeySetMV(int access, String name, String desc, MethodVisitor mv) { - super(ASM7, access, desc, mv); + super(ASM8, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/test/MongoModifyASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/test/MongoModifyASM.java new file mode 100644 index 000000000..c07472eec --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/test/MongoModifyASM.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.test; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.IASM; + +public class MongoModifyASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public MongoModifyASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf.hook_mongodb_enabled == false) { + return cv; + } + + if ("com/mongodb/connection/InternalConnection".equals(className)) { + return new InternalConnectionCV(cv, className); + } + return cv; + } + + static class InternalConnectionCV extends ClassVisitor implements Opcodes { + public String className; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions); + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + return super.visitMethod(newAccess, methodName, desc, signature, exceptions); + } + + public InternalConnectionCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/asm/test/ReactorModifyASM.java b/scouter.agent.java/src/main/java/scouter/agent/asm/test/ReactorModifyASM.java new file mode 100644 index 000000000..92ce1f659 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/asm/test/ReactorModifyASM.java @@ -0,0 +1,152 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.agent.asm.test; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Opcodes; +import scouter.agent.ClassDesc; +import scouter.agent.Configure; +import scouter.agent.asm.IASM; + +public class ReactorModifyASM implements IASM, Opcodes { + + private Configure conf = Configure.getInstance(); + + public ReactorModifyASM() { + } + + public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc) { + if (conf._hook_reactive_enabled == false) { + return cv; + } + + if ("reactor/core/publisher/OptimizableOperator".equals(className)) { + return new OptimizableOperatorCV(cv, className); + } + if ("reactor/core/publisher/MonoOnAssembly".equals(className)) { + return new MonoOnAssemblyCV(cv, className); + } + if ("reactor/core/publisher/FluxOnAssembly".equals(className)) { + return new FluxOnAssemblyCV(cv, className); + } + if ("reactor/core/publisher/FluxOnAssembly$AssemblySnapshot".equals(className)) { + return new AssemblySnapshotCV(cv, className); + } + return cv; + } + + static class OptimizableOperatorCV extends ClassVisitor implements Opcodes { + public String className; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + public OptimizableOperatorCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + } + + static class MonoOnAssemblyCV extends ClassVisitor implements Opcodes { + public String className; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + return super.visitField(newAccess, name, descriptor, signature, value); + } + + public MonoOnAssemblyCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + } + + static class FluxOnAssemblyCV extends ClassVisitor implements Opcodes { + public String className; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + return super.visitField(newAccess, name, descriptor, signature, value); + } + + public FluxOnAssemblyCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + } + + static class AssemblySnapshotCV extends ClassVisitor implements Opcodes { + public String className; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + int newAccess = access; + if ((access & Opcodes.ACC_PUBLIC) == 0) { + newAccess = access | Opcodes.ACC_PUBLIC; + } + return super.visitField(newAccess, name, descriptor, signature, value); + } + + public AssemblySnapshotCV(ClassVisitor cv, String className) { + super(ASM8, cv); + this.className = className; + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/counter/CounterExecutingManager.java b/scouter.agent.java/src/main/java/scouter/agent/counter/CounterExecutingManager.java index f2c792c6c..1b2f86160 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/counter/CounterExecutingManager.java +++ b/scouter.agent.java/src/main/java/scouter/agent/counter/CounterExecutingManager.java @@ -163,7 +163,8 @@ public void process(CounterBasket pw) throws Throwable { try { method.invoke(object, pw); } catch (Exception e) { - Logger.println("A111", object.getClass() + " " + method + " " + e); + Logger.println("A111", object.getClass() + " " + method + " " + e.getMessage(), e); + e.printStackTrace(); } } diff --git a/scouter.agent.java/src/main/java/scouter/agent/counter/meter/MeterInteractionManager.java b/scouter.agent.java/src/main/java/scouter/agent/counter/meter/MeterInteractionManager.java index e564bd046..ac4c3a7ed 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/counter/meter/MeterInteractionManager.java +++ b/scouter.agent.java/src/main/java/scouter/agent/counter/meter/MeterInteractionManager.java @@ -27,7 +27,9 @@ import static scouter.lang.counters.CounterConstants.INTR_API_INCOMING; import static scouter.lang.counters.CounterConstants.INTR_API_OUTGOING; import static scouter.lang.counters.CounterConstants.INTR_DB_CALL; +import static scouter.lang.counters.CounterConstants.INTR_ELASTICSEARCH_CALL; import static scouter.lang.counters.CounterConstants.INTR_KAFKA_CALL; +import static scouter.lang.counters.CounterConstants.INTR_MONGODB_CALL; import static scouter.lang.counters.CounterConstants.INTR_RABBITMQ_CALL; import static scouter.lang.counters.CounterConstants.INTR_NORMAL_INCOMING; import static scouter.lang.counters.CounterConstants.INTR_NORMAL_OUTGOING; @@ -48,6 +50,8 @@ public class MeterInteractionManager extends Thread { private static LinkedMap redisCallMeterMap = new LinkedMap().setMax(1000); private static LinkedMap kafkaCallMeterMap = new LinkedMap().setMax(1000); private static LinkedMap rabbitmqCallMeterMap = new LinkedMap().setMax(1000); + private static LinkedMap elasticSearchCallMeterMap = new LinkedMap().setMax(1000); + private static LinkedMap mongoDbCallMeterMap = new LinkedMap().setMax(1000); private MeterInteractionManager() { } @@ -92,6 +96,12 @@ public void run() { } else if (INTR_RABBITMQ_CALL.equals(type)) { rabbitmqCallMeterMap.put(key, meterInteraction); + + } else if (INTR_ELASTICSEARCH_CALL.equals(type)) { + elasticSearchCallMeterMap.put(key, meterInteraction); + + } else if (INTR_MONGODB_CALL.equals(type)) { + mongoDbCallMeterMap.put(key, meterInteraction); } } } @@ -194,6 +204,27 @@ public MeterInteraction getRabbitmqCallMeter(int fromHash, int toHash) { return meter; } + /** + * @return nullable + */ + public MeterInteraction getElasticSearchCallMeter(int fromHash, int toHash) { + Key key = new Key(fromHash, toHash); + MeterInteraction meter = elasticSearchCallMeterMap.get(key); + if (meter == null) { + queue.put(new Pair(INTR_ELASTICSEARCH_CALL, key)); + } + return meter; + } + + public MeterInteraction getMongoDbCallMeter(int fromHash, int toHash) { + Key key = new Key(fromHash, toHash); + MeterInteraction meter = mongoDbCallMeterMap.get(key); + if (meter == null) { + queue.put(new Pair(INTR_MONGODB_CALL, key)); + } + return meter; + } + public LinkedMap getApiOutgoingMeterMap() { return apiOutgoingMeterMap; } @@ -226,6 +257,13 @@ public LinkedMap getRabbitmqCallMeterMap() { return rabbitmqCallMeterMap; } + public LinkedMap getElasticSearchCallMeterMap() { + return elasticSearchCallMeterMap; + } + + public LinkedMap getMongoDbCallMeterMap() { + return mongoDbCallMeterMap; + } public static class Key { public int fromHash; diff --git a/scouter.agent.java/src/main/java/scouter/agent/counter/task/DebugService.java b/scouter.agent.java/src/main/java/scouter/agent/counter/task/DebugService.java index fd1769030..cff4e1444 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/counter/task/DebugService.java +++ b/scouter.agent.java/src/main/java/scouter/agent/counter/task/DebugService.java @@ -17,12 +17,6 @@ package scouter.agent.counter.task; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Enumeration; - import scouter.agent.Configure; import scouter.agent.Logger; import scouter.agent.counter.CounterBasket; @@ -37,6 +31,12 @@ import scouter.util.Hexa32; import scouter.util.SysJMX; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Enumeration; + public class DebugService { Configure conf = Configure.getInstance(); @@ -53,6 +53,7 @@ public void checkService(CounterBasket pw) { lastCheckStuckTime = now; } StringBuilder stuckMsg = new StringBuilder(); + //TODO reactive support Enumeration en = TraceContextManager.getContextEnumeration(); while (en.hasMoreElements()) { TraceContext ctx = en.nextElement(); @@ -71,6 +72,7 @@ public void checkService(CounterBasket pw) { } private void checkStcukService(TraceContext ctx, PrintWriter out, StringBuilder msg) { + //TODO reactive support if (conf.autodump_stuck_thread_ms <= 0) return; long etime = System.currentTimeMillis() - ctx.startTime; if (etime > conf.autodump_stuck_thread_ms) { diff --git a/scouter.agent.java/src/main/java/scouter/agent/counter/task/InteractionPerf.java b/scouter.agent.java/src/main/java/scouter/agent/counter/task/InteractionPerf.java index 71cfb0ab2..807a8e826 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/counter/task/InteractionPerf.java +++ b/scouter.agent.java/src/main/java/scouter/agent/counter/task/InteractionPerf.java @@ -118,7 +118,7 @@ public void collectKafkaCallInteractionCounter(InteractionCounterBasket basket) } @InteractionCounter(interval = 5000) - public void collecRabbitmqCallInteractionCounter(InteractionCounterBasket basket) { + public void collectRabbitmqCallInteractionCounter(InteractionCounterBasket basket) { if (!conf.counter_interaction_enabled) { return; } @@ -129,6 +129,30 @@ public void collecRabbitmqCallInteractionCounter(InteractionCounterBasket basket addInteractionsToBasket(basket, interactionType, rabbitmqCallMeterMap, periodSec); } + @InteractionCounter(interval = 5000) + public void collectElasticSearchCallInteractionCounter(InteractionCounterBasket basket) { + if (!conf.counter_interaction_enabled) { + return; + } + + int periodSec = 30; + String interactionType = CounterConstants.INTR_ELASTICSEARCH_CALL; + LinkedMap esMeterMap = MeterInteractionManager.getInstance().getElasticSearchCallMeterMap(); + addInteractionsToBasket(basket, interactionType, esMeterMap, periodSec); + } + + @InteractionCounter(interval = 5000) + public void collectMongoDbCallInteractionCounter(InteractionCounterBasket basket) { + if (!conf.counter_interaction_enabled) { + return; + } + + int periodSec = 30; + String interactionType = CounterConstants.INTR_MONGODB_CALL; + LinkedMap meterMap = MeterInteractionManager.getInstance().getMongoDbCallMeterMap(); + addInteractionsToBasket(basket, interactionType, meterMap, periodSec); + } + private void addInteractionsToBasket(InteractionCounterBasket basket, String interactionType, LinkedMap apiIncomingMeterMap, int periodSec) { Enumeration> entries = apiIncomingMeterMap.entries(); diff --git a/scouter.agent.java/src/main/java/scouter/agent/counter/task/MakeStack.java b/scouter.agent.java/src/main/java/scouter/agent/counter/task/MakeStack.java index a0961f836..fdb829a31 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/counter/task/MakeStack.java +++ b/scouter.agent.java/src/main/java/scouter/agent/counter/task/MakeStack.java @@ -8,6 +8,7 @@ import scouter.agent.proxy.ToolsMainFactory; import scouter.agent.trace.TraceContext; import scouter.agent.trace.TraceContextManager; +import scouter.agent.trace.TraceMain; import scouter.lang.pack.StackPack; import scouter.lang.step.DumpStep; @@ -16,99 +17,170 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; public class MakeStack { - static Configure conf = Configure.getInstance(); - - public long lastStackTime; - @Counter - public void make(CounterBasket pw) { - if (isPStackEnabled()== false){ - ToolsMainFactory.activeStack=false; - return; - } - long now = System.currentTimeMillis(); - if (now < lastStackTime + getSFAInterval()) - return; - lastStackTime = now; - StringWriter sw = new StringWriter(); - PrintWriter out = new PrintWriter(sw); - try { - ToolsMainFactory.threadDump(out); - } catch (Throwable e) { - } finally { - out.close(); - } - - String stack = sw.getBuffer().toString(); - - StackPack p = new StackPack(); - p.time = System.currentTimeMillis(); - p.objHash = conf.getObjHash(); - p.setStack(stack); - - DataProxy.sendDirect(p); - - long elapsed = (System.currentTimeMillis() - now); - Logger.trace("[SFA Counter Elasped]" + elapsed); - } - - public static long pstack_requested; - private boolean isPStackEnabled() { - return conf.sfa_dump_enabled || System.currentTimeMillis() < pstack_requested; - } - private long getSFAInterval() { - return conf.sfa_dump_interval_ms; - } - - - long lastStackTraceGenTime = 0; - @Counter - public void stackTraceStepGenerator(CounterBasket pw) { - if (!conf._psts_enabled){ - return; - } - - long now = System.currentTimeMillis(); - if (now < lastStackTraceGenTime + conf._psts_dump_interval_ms) { - return; - } - lastStackTraceGenTime = now; - - ThreadMXBean tmxBean = ManagementFactory.getThreadMXBean(); - Enumeration en = TraceContextManager.getContextEnumeration(); - while (en.hasMoreElements()) { - TraceContext ctx = en.nextElement(); - if(ctx == null || ctx.threadId <= 0) { - continue; - } - - ThreadInfo tInfo = tmxBean.getThreadInfo(ctx.threadId, 50); - if (tInfo == null) continue; - - StackTraceElement[] elements = tInfo.getStackTrace(); - int length = elements.length; - int[] stacks = new int[length]; - - for(int i=0; i en = TraceContextManager.getContextEnumeration(); + while (en.hasMoreElements()) { + TraceContext ctx = en.nextElement(); + if (ctx != null) { + if (ctx.isReactiveStarted) { + reactiveStepDump(tmxBean, ctx); + } else { + stepDump(tmxBean, ctx); + } + } + } + long elapsed = (System.currentTimeMillis() - now); + } + + private void stepDump(ThreadMXBean tmxBean, TraceContext ctx) { + if (ctx == null || ctx.threadId <= 0) { + return; + } + + ThreadInfo tInfo = tmxBean.getThreadInfo(ctx.threadId, 50); + if (tInfo == null) return; + + StackTraceElement[] elements = tInfo.getStackTrace(); + int length = elements.length; + int[] stacks = new int[length]; + + for (int i = 0; i < length; i++) { + stacks[i] = DataProxy.sendStackElement(elements[i]); + } + DumpStep dumpStep = new DumpStep(); + dumpStep.start_time = (int) (System.currentTimeMillis() - ctx.startTime); + dumpStep.stacks = stacks; + dumpStep.threadId = ctx.threadId; + dumpStep.threadName = tInfo.getThreadName(); + dumpStep.threadState = tInfo.getThreadState().toString(); + dumpStep.lockOwnerId = tInfo.getLockOwnerId(); + dumpStep.lockName = tInfo.getLockName(); + dumpStep.lockOwnerName = tInfo.getLockOwnerName(); + + ctx.temporaryDumpSteps.offer(dumpStep); + ctx.hasDumpStack = true; + } + + private void reactiveStepDump(ThreadMXBean tmxBean, TraceContext ctx) { + if (ctx == null) { + return; + } + + long now = System.currentTimeMillis(); + + List stacks = new ArrayList(); + + DumpStep dumpStep = new DumpStep(); + dumpStep.start_time = (int) (System.currentTimeMillis() - ctx.startTime); + + long threadId = TraceContextManager.getReactiveThreadId(ctx.txid); + if (threadId != 0) { + ThreadInfo tInfo = tmxBean.getThreadInfo(ctx.threadId, 50); + if (tInfo != null) { + StackTraceElement[] elements = tInfo.getStackTrace(); + for (StackTraceElement element : elements) { + stacks.add(DataProxy.sendStackElement(element)); + } + + dumpStep.threadId = threadId; + dumpStep.threadName = tInfo.getThreadName(); + dumpStep.threadState = tInfo.getThreadState().toString(); + dumpStep.lockOwnerId = tInfo.getLockOwnerId(); + dumpStep.lockName = tInfo.getLockName(); + dumpStep.lockOwnerName = tInfo.getLockOwnerName(); + } + } + + if (ctx.scannables != null) { + Enumeration en = ctx.scannables.values(); + stacks.add(DataProxy.sendStackElement("<<<<<<<<<< currently existing subscribes >>>>>>>>>>")); + while (en.hasMoreElements()) { + TraceContext.TimedScannable ts = en.nextElement(); + if (ts == null) { + return; + } + String dumpScannable = TraceMain.reactiveSupport.dumpScannable(ctx, ts, now); + stacks.add(DataProxy.sendStackElement(dumpScannable)); + } + + dumpStep.stacks = convertIntegers(stacks); + } + ctx.temporaryDumpSteps.offer(dumpStep); + ctx.hasDumpStack = true; + } + + private static int[] convertIntegers(List integers) { + int[] ret = new int[integers.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = integers.get(i); + } + return ret; + } + } diff --git a/scouter.agent.java/src/main/java/scouter/agent/counter/task/TomcatJMXPerf.java b/scouter.agent.java/src/main/java/scouter/agent/counter/task/TomcatJMXPerf.java index 7d3c419ce..a43f2e3a9 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/counter/task/TomcatJMXPerf.java +++ b/scouter.agent.java/src/main/java/scouter/agent/counter/task/TomcatJMXPerf.java @@ -1,8 +1,8 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -12,9 +12,10 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License. + * limitations under the License. */ package scouter.agent.counter.task; + import scouter.agent.Configure; import scouter.agent.Logger; import scouter.agent.ObjTypeDetector; @@ -35,229 +36,267 @@ import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.util.*; + public class TomcatJMXPerf { - HashMap meters = new HashMap(); - HashMap lastValues = new HashMap(); - private static HashSet deltas = new HashSet(); - static { - deltas.add(CounterConstants.REQUESTPROCESS_BYTES_RECEIVED); - deltas.add(CounterConstants.REQUESTPROCESS_BYTES_SENT); - deltas.add(CounterConstants.REQUESTPROCESS_ERROR_COUNT); - deltas.add(CounterConstants.REQUESTPROCESS_PROCESSING_TIME); - deltas.add(CounterConstants.REQUESTPROCESS_REQUEST_COUNT); - ConfObserver.add("TomcatJMXPerf", new Runnable() { - public void run() { - ObjTypeDetector.dirtyConfig = true; - } - }); - } - private long getDelta(MeterKey key, Long newValue) { - Long oldValue = lastValues.put(key, newValue); - return oldValue == null ? 0 : newValue.longValue() - oldValue.longValue(); - } - private MeterResource getMeter(MeterKey key) { - MeterResource meter = meters.get(key); - if (meter == null) { - meter = new MeterResource(); - meters.put(key, meter); - } - return meter; - } - static class MeterKey { - int mbeanHash; - String counter; - public MeterKey(int mbeanHash, String counter) { - this.mbeanHash = mbeanHash; - this.counter = counter; - } - public int hashCode() { - return mbeanHash ^ counter.hashCode(); - } - public boolean equals(Object obj) { - if (obj instanceof MeterKey) { - MeterKey key = (MeterKey) obj; - return (this.mbeanHash == key.mbeanHash) && (this.counter.equals(key.counter)); - } - return false; - } - } + HashMap meters = new HashMap(); + HashMap lastValues = new HashMap(); + private static HashSet deltas = new HashSet(); + + static { + deltas.add(CounterConstants.REQUESTPROCESS_BYTES_RECEIVED); + deltas.add(CounterConstants.REQUESTPROCESS_BYTES_SENT); + deltas.add(CounterConstants.REQUESTPROCESS_ERROR_COUNT); + deltas.add(CounterConstants.REQUESTPROCESS_PROCESSING_TIME); + deltas.add(CounterConstants.REQUESTPROCESS_REQUEST_COUNT); + ConfObserver.add("TomcatJMXPerf", new Runnable() { + public void run() { + ObjTypeDetector.dirtyConfig = true; + } + }); + } + + private long getDelta(MeterKey key, Long newValue) { + Long oldValue = lastValues.put(key, newValue); + return oldValue == null ? 0 : newValue.longValue() - oldValue.longValue(); + } + + private MeterResource getMeter(MeterKey key) { + MeterResource meter = meters.get(key); + if (meter == null) { + meter = new MeterResource(); + meters.put(key, meter); + } + return meter; + } - private MBeanServer server; - List beanList = new ArrayList(); - public long collectCnt = 0; + static class MeterKey { + int mbeanHash; + String counter; - @Counter - public void process(CounterBasket pw) { - if (conf.jmx_counter_enabled == false || - (CounterConstants.TOMCAT.equals(ObjTypeDetector.objType) == false - && CounterConstants.TOMCAT.equals(ObjTypeDetector.drivedType) == false)) { - return; - } + public MeterKey(int mbeanHash, String counter) { + this.mbeanHash = mbeanHash; + this.counter = counter; + } - getMBeanServer(); - if ((collectCnt <= 40 && collectCnt % 5 == 0) || ObjTypeDetector.dirtyConfig) { - if (ObjTypeDetector.dirtyConfig) { - AgentHeartBeat.clearSubObjects(); - ObjTypeDetector.dirtyConfig = false; - } - getMBeanList(); - } - collectCnt++; - for (MBeanObj beanObj : beanList) { + public int hashCode() { + return mbeanHash ^ counter.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof MeterKey) { + MeterKey key = (MeterKey) obj; + return (this.mbeanHash == key.mbeanHash) && (this.counter.equals(key.counter)); + } + return false; + } + } + + private MBeanServer server; + List beanList = new ArrayList(); + public long collectCnt = 0; + + @Counter + public void process(CounterBasket pw) { + if (conf.jmx_counter_enabled == false || + (CounterConstants.TOMCAT.equals(ObjTypeDetector.objType) == false + && CounterConstants.TOMCAT.equals(ObjTypeDetector.drivedType) == false)) { + return; + } + + getMBeanServer(); + if ((collectCnt <= 40 && collectCnt % 5 == 0) || ObjTypeDetector.dirtyConfig) { + if (ObjTypeDetector.dirtyConfig) { + AgentHeartBeat.clearSubObjects(); + ObjTypeDetector.dirtyConfig = false; + } + getMBeanList(); + } + collectCnt++; + for (MBeanObj beanObj : beanList) { if (errors.contains(beanObj.attrName)) continue; - if (beanObj.valueType == ValueEnum.DECIMAL) { - try { - MeterKey key = new MeterKey(beanObj.mbeanHash, beanObj.counter); - long v = CastUtil.clong(server.getAttribute(beanObj.mbean, beanObj.attrName)); - if (deltas.contains(beanObj.counter)) { - v = getDelta(key, v); - MeterResource meter = getMeter(key); - meter.add(v); - v = (long) meter.getSum(60); - long sum = (long) meter.getSum(300) / 5; - pw.getPack(beanObj.objName, TimeTypeEnum.REALTIME).add(beanObj.counter, new DecimalValue(v)); - pw.getPack(beanObj.objName, TimeTypeEnum.FIVE_MIN).add(beanObj.counter, new DecimalValue(sum)); - } else { - MeterResource meter = getMeter(key); - meter.add(v); - double avg = meter.getAvg(300); - FloatValue avgValue = new FloatValue((float) avg); - pw.getPack(beanObj.objName, TimeTypeEnum.REALTIME).add(beanObj.counter, new DecimalValue(v)); - pw.getPack(beanObj.objName, TimeTypeEnum.FIVE_MIN).add(beanObj.counter, avgValue); - } - } catch (Exception e) { - errors.add(beanObj.attrName); - collectCnt = 0; - Logger.println("A902", e); - } - } - } - // long cpu2 = SysJMX.getCurrentThreadCPU(); - } - private HashSet errors = new HashSet(); - private void getMBeanServer() { - if (server == null) { - server = ManagementFactory.getPlatformMBeanServer(); - } - } - Configure conf = Configure.getInstance(); - private void getMBeanList() { - beanList.clear(); - Set mbeans = server.queryNames(null, null); - for (final ObjectName mbean : mbeans) { - String type = mbean.getKeyProperty("type"); - String connectionpool = mbean.getKeyProperty("connectionpool"); - if (type == null) { - continue; - } - if ("GlobalRequestProcessor".equals(type)) { - String port = mbean.getKeyProperty("name"); - try { - String objName = conf.getObjName() + "/" + checkObjName(port); - String objType = getReqProcType(); - AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName); - add(objName, mbean, objType, ValueEnum.DECIMAL, "bytesReceived", - CounterConstants.REQUESTPROCESS_BYTES_RECEIVED); - add(objName, mbean, objType, ValueEnum.DECIMAL, "bytesSent", - CounterConstants.REQUESTPROCESS_BYTES_SENT); - add(objName, mbean, objType, ValueEnum.DECIMAL, "errorCount", - CounterConstants.REQUESTPROCESS_ERROR_COUNT); - add(objName, mbean, objType, ValueEnum.DECIMAL, "processingTime", - CounterConstants.REQUESTPROCESS_PROCESSING_TIME); - add(objName, mbean, objType, ValueEnum.DECIMAL, "requestCount", - CounterConstants.REQUESTPROCESS_REQUEST_COUNT); - } catch (Exception e) { - } - } else if ("DataSource".equals(type) && connectionpool == null) { // datasource - String name = mbean.getKeyProperty("name"); - if (StringUtil.isNotEmpty(name)) { - try { - String context = mbean.getKeyProperty("context"); - if (context != null && context.length() > 0) { - context = context.substring(1); - } - if (StringUtil.isNotEmpty(context)) { - name = context + "_" + name; - } - String objName = conf.getObjName() + "/" + checkObjName(name); - String objType = getDataSourceType(); - AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName); - add(objName, mbean, objType, ValueEnum.DECIMAL, "numActive", - CounterConstants.DATASOURCE_CONN_ACTIVE); - add(objName, mbean, objType, ValueEnum.DECIMAL, "numIdle", - CounterConstants.DATASOURCE_CONN_IDLE); - add(objName, mbean, objType, ValueEnum.DECIMAL, "maxActive", - CounterConstants.DATASOURCE_CONN_MAX); - // for tomcat 5.5 + - // attribute name is changed from maxActive to maxTotal. (reported from zeroty : https://github.com/zeroty) - add(objName, mbean, objType, ValueEnum.DECIMAL, "maxTotal", - CounterConstants.DATASOURCE_CONN_MAX); - } catch (Exception e) { - } - } - } - } - } - private String getReqProcType() { - if (Configure.getInstance().obj_type_inherit_to_child_enabled) { - return Configure.getInstance().obj_type + "_req"; - } - return CounterConstants.REQUESTPROCESS; - } - private String getDataSourceType() { - if (Configure.getInstance().obj_type_inherit_to_child_enabled) { - return Configure.getInstance().obj_type + "_ds"; - } - return CounterConstants.DATASOURCE; - } - private void add(String objName, ObjectName mbean, String type, byte decimal, String attrName, String counterName) { - if (errors.contains(attrName)) - return; - MBeanObj cObj = new MBeanObj(objName, mbean, type, ValueEnum.DECIMAL, attrName, counterName); - beanList.add(cObj); - } - private static String checkObjName(String name) { - StringBuffer sb = new StringBuffer(); - char[] charArray = name.toCharArray(); - for (int i = 0; i < charArray.length; i++) { - switch (charArray[i]) { - case '-': - case '_': - sb.append(charArray[i]); - break; - case '/': - sb.append('_'); - break; - default: - if (Character.isLetterOrDigit(charArray[i])) { - sb.append(charArray[i]); - } - } - } - return sb.toString(); - } - class MBeanObj { - public int mbeanHash; - public String objName; - public ObjectName mbean; - public String objType; - public byte valueType; - public String attrName; - public String counter; - public MBeanObj(String objName, ObjectName mbean, String objType, byte valueType, String attrName, - String counter) { - this.objName = objName; - this.mbean = mbean; - this.mbeanHash = HashUtil.hash(mbean.toString()); - this.objType = objType; - this.valueType = valueType; - this.attrName = attrName; - this.counter = counter; - } - @Override - public String toString() { - return "MBeanObj [objName=" + objName + ", mbean=" + mbean + ", objType=" + objType + ", valueType=" - + valueType + ", attrName=" + attrName + ", counter=" + counter + "]"; - } - } + if (beanObj.valueType == ValueEnum.DECIMAL) { + try { + MeterKey key = new MeterKey(beanObj.mbeanHash, beanObj.counter); + long v = CastUtil.clong(server.getAttribute(beanObj.mbean, beanObj.attrName)); + if (deltas.contains(beanObj.counter)) { + v = getDelta(key, v); + MeterResource meter = getMeter(key); + meter.add(v); + v = (long) meter.getSum(60); + long sum = (long) meter.getSum(300) / 5; + pw.getPack(beanObj.objName, TimeTypeEnum.REALTIME).add(beanObj.counter, new DecimalValue(v)); + pw.getPack(beanObj.objName, TimeTypeEnum.FIVE_MIN).add(beanObj.counter, new DecimalValue(sum)); + } else { + MeterResource meter = getMeter(key); + meter.add(v); + double avg = meter.getAvg(300); + FloatValue avgValue = new FloatValue((float) avg); + pw.getPack(beanObj.objName, TimeTypeEnum.REALTIME).add(beanObj.counter, new DecimalValue(v)); + pw.getPack(beanObj.objName, TimeTypeEnum.FIVE_MIN).add(beanObj.counter, avgValue); + } + } catch (Exception e) { + errors.add(beanObj.attrName); + collectCnt = 0; + Logger.println("A902", e); + } + } + } + // long cpu2 = SysJMX.getCurrentThreadCPU(); + } + + private HashSet errors = new HashSet(); + + private void getMBeanServer() { + if (server == null) { + server = ManagementFactory.getPlatformMBeanServer(); + } + } + + Configure conf = Configure.getInstance(); + + private void getMBeanList() { + beanList.clear(); + Set mbeans = server.queryNames(null, null); + for (final ObjectName mbean : mbeans) { + String type = mbean.getKeyProperty("type"); + String connectionpool = mbean.getKeyProperty("connectionpool"); + if (type == null) { + continue; + } + if ("GlobalRequestProcessor".equals(type)) { + String port = mbean.getKeyProperty("name"); + try { + String objName = conf.getObjName() + "/" + checkObjName(port); + String objType = getReqProcType(); + AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName); + add(objName, mbean, objType, ValueEnum.DECIMAL, "bytesReceived", + CounterConstants.REQUESTPROCESS_BYTES_RECEIVED); + add(objName, mbean, objType, ValueEnum.DECIMAL, "bytesSent", + CounterConstants.REQUESTPROCESS_BYTES_SENT); + add(objName, mbean, objType, ValueEnum.DECIMAL, "errorCount", + CounterConstants.REQUESTPROCESS_ERROR_COUNT); + add(objName, mbean, objType, ValueEnum.DECIMAL, "processingTime", + CounterConstants.REQUESTPROCESS_PROCESSING_TIME); + add(objName, mbean, objType, ValueEnum.DECIMAL, "requestCount", + CounterConstants.REQUESTPROCESS_REQUEST_COUNT); + } catch (Exception e) { + } + } else if ("DataSource".equals(type) && connectionpool == null) { // datasource + try { + String modelerType = CastUtil.cString(server.getAttribute(mbean, "modelerType")); + if ("com.zaxxer.hikari.HikariDataSource".equals(modelerType)) { + continue; + } + String name = mbean.getKeyProperty("name"); + if (StringUtil.isNotEmpty(name)) { + String context = mbean.getKeyProperty("context"); + if (context != null && context.length() > 0) { + context = context.substring(1); + } + if (StringUtil.isNotEmpty(context)) { + name = context + "_" + name; + } + String objName = conf.getObjName() + "/" + checkObjName(name); + String objType = getDataSourceType(); + AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName); + add(objName, mbean, objType, ValueEnum.DECIMAL, "numActive", + CounterConstants.DATASOURCE_CONN_ACTIVE); + add(objName, mbean, objType, ValueEnum.DECIMAL, "numIdle", + CounterConstants.DATASOURCE_CONN_IDLE); + add(objName, mbean, objType, ValueEnum.DECIMAL, "maxActive", + CounterConstants.DATASOURCE_CONN_MAX); + // for tomcat 5.5 + + // attribute name is changed from maxActive to maxTotal. (reported from zeroty : https://github.com/zeroty) + add(objName, mbean, objType, ValueEnum.DECIMAL, "maxTotal", + CounterConstants.DATASOURCE_CONN_MAX); + } + } catch (Exception e) { + } + } else if ("com.zaxxer.hikari".equals(mbean.getDomain()) && type.startsWith("PoolConfig (")) { + try { + final String poolName = CastUtil.cString(server.getAttribute(mbean, "PoolName")); + final String objName = conf.getObjName() + "/" + checkObjName(poolName); + final String objType = getDataSourceType(); + AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName); + ObjectName hikariMBean = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")"); + add(objName, hikariMBean, objType, ValueEnum.DECIMAL, "ActiveConnections", + CounterConstants.DATASOURCE_CONN_ACTIVE); + add(objName, hikariMBean, objType, ValueEnum.DECIMAL, "IdleConnections", + CounterConstants.DATASOURCE_CONN_IDLE); + add(objName, hikariMBean, objType, ValueEnum.DECIMAL, "TotalConnections", + CounterConstants.DATASOURCE_CONN_MAX); + } catch (Exception e) { + } + } + } + } + + private String getReqProcType() { + if (Configure.getInstance().obj_type_inherit_to_child_enabled) { + return Configure.getInstance().obj_type + "_req"; + } + return CounterConstants.REQUESTPROCESS; + } + + private String getDataSourceType() { + if (Configure.getInstance().obj_type_inherit_to_child_enabled) { + return Configure.getInstance().obj_type + "_ds"; + } + return CounterConstants.DATASOURCE; + } + + private void add(String objName, ObjectName mbean, String type, byte decimal, String attrName, String counterName) { + if (errors.contains(attrName)) + return; + MBeanObj cObj = new MBeanObj(objName, mbean, type, ValueEnum.DECIMAL, attrName, counterName); + beanList.add(cObj); + } + + private static String checkObjName(String name) { + StringBuffer sb = new StringBuffer(); + char[] charArray = name.toCharArray(); + for (int i = 0; i < charArray.length; i++) { + switch (charArray[i]) { + case '-': + case '_': + sb.append(charArray[i]); + break; + case '/': + sb.append('_'); + break; + default: + if (Character.isLetterOrDigit(charArray[i])) { + sb.append(charArray[i]); + } + } + } + return sb.toString(); + } + + class MBeanObj { + public int mbeanHash; + public String objName; + public ObjectName mbean; + public String objType; + public byte valueType; + public String attrName; + public String counter; + + public MBeanObj(String objName, ObjectName mbean, String objType, byte valueType, String attrName, + String counter) { + this.objName = objName; + this.mbean = mbean; + this.mbeanHash = HashUtil.hash(mbean.toString()); + this.objType = objType; + this.valueType = valueType; + this.attrName = attrName; + this.counter = counter; + } + + @Override + public String toString() { + return "MBeanObj [objName=" + objName + ", mbean=" + mbean + ", objType=" + objType + ", valueType=" + + valueType + ", attrName=" + attrName + ", counter=" + counter + "]"; + } + } } diff --git a/scouter.agent.java/src/main/java/scouter/agent/netio/data/DataProxy.java b/scouter.agent.java/src/main/java/scouter/agent/netio/data/DataProxy.java index a004378d0..3dcfb037b 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/netio/data/DataProxy.java +++ b/scouter.agent.java/src/main/java/scouter/agent/netio/data/DataProxy.java @@ -362,4 +362,13 @@ public static int sendStackElement(StackTraceElement ste) { udpCollect.add(new TextPack(TextTypes.STACK_ELEMENT, hash, ste.toString())); return hash; } + public static int sendStackElement(String ste) { + int hash = ste.hashCode(); + if (stackElement.contains(hash)) { + return hash; + } + stackElement.put(hash); + udpCollect.add(new TextPack(TextTypes.STACK_ELEMENT, hash, ste)); + return hash; + } } diff --git a/scouter.agent.java/src/main/java/scouter/agent/netio/request/handle/AgentThread.java b/scouter.agent.java/src/main/java/scouter/agent/netio/request/handle/AgentThread.java index a5497b7a6..5bc0ff7a1 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/netio/request/handle/AgentThread.java +++ b/scouter.agent.java/src/main/java/scouter/agent/netio/request/handle/AgentThread.java @@ -24,10 +24,15 @@ import scouter.agent.proxy.ToolsMainFactory; import scouter.agent.trace.TraceContext; import scouter.agent.trace.TraceContextManager; +import scouter.agent.trace.TraceMain; import scouter.agent.util.DumpUtil; import scouter.lang.pack.MapPack; import scouter.lang.pack.Pack; -import scouter.lang.value.*; +import scouter.lang.value.BooleanValue; +import scouter.lang.value.DecimalValue; +import scouter.lang.value.ListValue; +import scouter.lang.value.NullValue; +import scouter.lang.value.TextValue; import scouter.util.CastUtil; import scouter.util.Hexa32; import scouter.util.SysJMX; @@ -36,65 +41,105 @@ import java.io.IOException; import java.util.Enumeration; -import static scouter.net.RequestCmd.*; +import static scouter.net.RequestCmd.OBJECT_ACTIVE_SERVICE_LIST; +import static scouter.net.RequestCmd.OBJECT_THREAD_CONTROL; +import static scouter.net.RequestCmd.OBJECT_THREAD_DETAIL; +import static scouter.net.RequestCmd.OBJECT_THREAD_DUMP; +import static scouter.net.RequestCmd.OBJECT_THREAD_LIST; +import static scouter.net.RequestCmd.PSTACK_ON; +import static scouter.net.RequestCmd.TRIGGER_ACTIVE_SERVICE_LIST; +import static scouter.net.RequestCmd.TRIGGER_DUMP_REASON; +import static scouter.net.RequestCmd.TRIGGER_THREAD_DUMP; +import static scouter.net.RequestCmd.TRIGGER_THREAD_DUMPS_FROM_CONDITIONS; +import static scouter.net.RequestCmd.TRIGGER_THREAD_LIST; public class AgentThread { @RequestHandler(OBJECT_THREAD_DETAIL) public Pack threadDetail(Pack param) { MapPack paramPack = (MapPack) param; - long thread = paramPack.getLong("id"); + long threadId = paramPack.getLong("id"); long txid = paramPack.getLong("txid"); - MapPack p; - TraceContext ctx; + MapPack p = new MapPack(); + TraceContext ctx = TraceContextManager.getContextByTxid(txid); + if (ctx == null) { + p.put("Thread Name", new TextValue("[No Thread] End")); + p.put("State", new TextValue("end")); + return p; + } - if(thread != 0L) { - p = ThreadUtil.getThreadDetail(thread); - ctx = TraceContextManager.getContext(thread); + if (ctx.isReactiveStarted) { + threadId = TraceContextManager.getReactiveThreadId(txid); + } - if (ctx != null) { - p.put("Service Txid", new TextValue(Hexa32.toString32(ctx.txid))); - p.put("Service Name", new TextValue(ctx.serviceName)); - long etime = System.currentTimeMillis() - ctx.startTime; - p.put("Service Elapsed", new DecimalValue(etime)); - String sql = ctx.sqltext; - if (sql != null) { - p.put("SQL", sql); - } - String subcall = ctx.apicall_name; - if (subcall != null) { - p.put("Subcall", subcall); - } - } + p.put("Service Txid", new TextValue(Hexa32.toString32(ctx.txid))); + p.put("Service Name", new TextValue(ctx.serviceName)); + long etime = System.currentTimeMillis() - ctx.startTime; + p.put("Service Elapsed", new DecimalValue(etime)); + String sql = ctx.sqltext; + if (sql != null) { + p.put("SQL", sql); + } + String subcall = ctx.apicall_name; + if (subcall != null) { + p.put("Subcall", subcall); + } - } else { - p = new MapPack(); - ctx = TraceContextManager.getDeferredContext(txid); - p.put("Thread Id", new DecimalValue(0L)); + if(threadId != 0L) { + p = ThreadUtil.appendThreadDetail(threadId, p); - if (ctx != null) { + } else { + TraceContext deferredContext = TraceContextManager.getDeferredContext(txid); + if (deferredContext != null) { p.put("Thread Name", new TextValue("[No Thread] wait on deferred queue")); - p.put("State", new TextValue("n/a")); - - p.put("Service Txid", new TextValue(Hexa32.toString32(ctx.txid))); - p.put("Service Name", new TextValue(ctx.serviceName)); - long etime = System.currentTimeMillis() - ctx.startTime; - p.put("Service Elapsed", new DecimalValue(etime)); - } else { - p.put("Thread Name", new TextValue("[No Thread] End")); - p.put("State", new TextValue("end")); + p.put("Thread Name", new TextValue("No dedicated thread")); } + p.put("Thread Id", new DecimalValue(0L)); + p.put("State", new TextValue("n/a")); + } + + if (ctx.isReactiveStarted) { + String stack = p.getText("Stack Trace"); + if (stack == null) { + stack = ""; + } + stack = stack + "\n" + getUnfinishedReactiveStepsAsDumpString(ctx); + p.put("Stack Trace", new TextValue(stack)); } return p; } + + private String getUnfinishedReactiveStepsAsDumpString(TraceContext ctx) { + if (ctx == null) { + return null; + } + + long now = System.currentTimeMillis(); + StringBuilder builder = new StringBuilder(200) + .append("<<<<<<<<<< currently existing subscribes >>>>>>>>>>").append("\n"); + + if (ctx.scannables != null) { + Enumeration en = ctx.scannables.values(); + while (en.hasMoreElements()) { + TraceContext.TimedScannable ts = en.nextElement(); + if (ts == null) { + break; + } + String dumpScannable = TraceMain.reactiveSupport.dumpScannable(ctx, ts, now); + builder.append(dumpScannable).append("\n"); + } + } + return builder.toString(); + } + @RequestHandler(OBJECT_THREAD_CONTROL) public Pack threadKill(Pack param) { long thread = ((MapPack) param).getLong("id"); String action = ((MapPack) param).getText("action"); // 쓰레드 상세 화면에서 쓰레드를 제어한다. - TraceContext ctx = TraceContextManager.getContext(thread); + TraceContext ctx = TraceContextManager.getContextByThreadId(thread); try { if (ctx != null) { if ("interrupt".equalsIgnoreCase(action)) { @@ -135,7 +180,7 @@ public Pack threadList(Pack param) { ListValue service = mpack.newList("service"); for (int i = 0; i < ids.size(); i++) { long tid = CastUtil.clong(ids.get(i)); - TraceContext ctx = TraceContextManager.getContext(tid); + TraceContext ctx = TraceContextManager.getContextByThreadId(tid); if (ctx != null) { txid.add(new TextValue(Hexa32.toString32(ctx.txid))); service.add(new TextValue(ctx.serviceName)); @@ -165,15 +210,33 @@ public Pack activeThreadList(Pack param) { ListValue subcall = rPack.newList("subcall"); ListValue login = rPack.newList("login"); ListValue desc = rPack.newList("desc"); + Enumeration en = TraceContextManager.getContextEnumeration(); while (en.hasMoreElements()) { TraceContext ctx = en.nextElement(); if (ctx == null) { continue; } - id.add(ctx.thread.getId()); - name.add(ctx.thread.getName()); - stat.add(ctx.thread.getState().name()); + if (!ctx.isReactiveStarted) { + id.add(ctx.thread.getId()); + name.add(ctx.thread.getName()); + stat.add(ctx.thread.getState().name()); + } else { + if (Configure.getInstance()._psts_progressive_reactor_thread_trace_enabled) { + id.add(TraceContextManager.getReactiveThreadId(ctx.txid)); + } else { + id.add(0); + } + name.add("omit on reactive"); + stat.add("n/a"); + } + try { + cpu.add(SysJMX.getThreadCpuTime(ctx.thread)); + } catch (Throwable th) { + Logger.println("A128", th); + cpu.add(0L); + } + txid.add(new TextValue(Hexa32.toString32(ctx.txid))); service.add(new TextValue(ctx.serviceName)); ip.add(ctx.remoteIp); @@ -181,12 +244,6 @@ public Pack activeThreadList(Pack param) { elapsed.add(new DecimalValue(etime)); sql.add(ctx.sqltext); subcall.add(ctx.apicall_name); - try { - cpu.add(SysJMX.getThreadCpuTime(ctx.thread)); - } catch (Throwable th) { - Logger.println("A128", th); - cpu.add(0L); - } login.add(ctx.login); desc.add(ctx.desc); } diff --git a/scouter.agent.java/src/main/java/scouter/agent/plugin/AbstractPlugin.java b/scouter.agent.java/src/main/java/scouter/agent/plugin/AbstractPlugin.java index 5a5a9bbc2..49576db70 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/plugin/AbstractPlugin.java +++ b/scouter.agent.java/src/main/java/scouter/agent/plugin/AbstractPlugin.java @@ -39,8 +39,6 @@ public static Object invokeMethod(Object o, String methodName) throws NoSuchMeth public static Object invokeMethod(Object o, String methodName, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { int argsSize = args.length; - StringBuilder signature = new StringBuilder(o.getClass().getName()).append(":").append(methodName).append("():"); - Class[] argClazzes = new Class[argsSize]; for(int i=0; i { + private static final ThreadLocal COROUTINE_DEBUGGING_ID = new ThreadLocal(); + + private static final LongKeyMap CID_TRACE_CONTEXT = new LongKeyMap(); + + public static void setCoroutineDebuggingId(Long id) { + COROUTINE_DEBUGGING_ID.set(id); + } + + public static Long getCoroutineDebuggingId() { + return COROUTINE_DEBUGGING_ID.get(); + } + + public static void releaseCoroutineId() { + COROUTINE_DEBUGGING_ID.remove(); + } + + + public T get() { + Long coroutineId = getCoroutineDebuggingId(); + if (coroutineId == null) { + return null; + } + return (T) CID_TRACE_CONTEXT.get(coroutineId); + } + + public T get(long id) { + return (T) CID_TRACE_CONTEXT.get(id); + } + + public void put(T obj) { + Long coroutineId = getCoroutineDebuggingId(); + CID_TRACE_CONTEXT.put(coroutineId, obj); + } + + public void clear() { + Long coroutineId = getCoroutineDebuggingId(); + if (coroutineId == null) { + return; + } + CID_TRACE_CONTEXT.put(coroutineId, null); + releaseCoroutineId(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/StepTransferMap.java b/scouter.agent.java/src/main/java/scouter/agent/trace/StepTransferMap.java new file mode 100644 index 000000000..07f53eccc --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/StepTransferMap.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.agent.trace; + +import scouter.lang.step.Step; +import scouter.util.IntKeyLinkedMap; + +public class StepTransferMap { + + public static class ID { + public TraceContext ctx; + public Step step; + public Object option; + + public ID(TraceContext ctx, Step step, Object option) { + this.ctx = ctx; + this.step = step; + this.option = option; + } + } + + private static IntKeyLinkedMap map = new IntKeyLinkedMap().setMax(2001); + + public static ID makeID(TraceContext ctx, Step step) { + return new ID(ctx, step, null); + } + public static void put(int hash, TraceContext ctx, Step step) { + map.put(hash, new ID(ctx,step, null)); + } + public static void put(int hash, TraceContext ctx, Step step, Object option) { + map.put(hash, new ID(ctx,step, option)); + } + + public static void remove(int hash) { + map.remove(hash); + } + + public static ID get(int hash) { + return map.get(hash); + } + + +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceApiCall.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceApiCall.java index 8195f609e..33080e976 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceApiCall.java +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceApiCall.java @@ -287,6 +287,9 @@ public String getHeader(Object o, String key) { public String getResponseHeader(Object o, String key) { return null; } + public int getResponseStatusCode(Object o) { + return 200; + } public void addHeader(Object o, String key, String value) { } }; @@ -345,6 +348,33 @@ public static void endCreateSpringRestTemplateRequest(Object _this, Object oRtn) } } + public static void webClientInfo(Object bodyInserter, Object clientHttpRequest) { + if (!conf.trace_interservice_enabled) { + return; + } + ApiCallTraceHelper.webClientInfo(bodyInserter, clientHttpRequest); + } + + public static void endWebClientApicall(Object exchangeFunction, Object clientResponse) { + if (!conf.trace_interservice_enabled) { + return; + } + LocalContext localContext = ApiCallTraceHelper.webClientProcessEnd(exchangeFunction, clientResponse); + if (localContext == null) { + return; + } + Object option = localContext.option; + localContext.option = null; + Throwable throwable = null; + if (option instanceof Integer) { + int statusCode = (Integer) option; + if (statusCode >= 400) { + throwable = new RuntimeException("WebClient response code: " + statusCode); + } + } + endApicall(localContext, clientResponse, throwable); + } + public static void setCalleeToCtxInHttpClientResponse(Object _this, Object res) { if (!conf.trace_interservice_enabled) { return; @@ -426,7 +456,7 @@ public static void initImmutableJavaHttpRequest(Object requestBuilder) { if(ctx == null) return; try { - ApiCallTraceHelper.setCalleeToCtxJavaHttpRequest(ctx, requestBuilder); + ApiCallTraceHelper.setTransferToCtxJavaHttpRequest(ctx, requestBuilder); } catch (Exception e) { e.printStackTrace(); } diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContext.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContext.java index 7dcb51c77..72e775fd9 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContext.java +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContext.java @@ -17,12 +17,14 @@ package scouter.agent.trace; +import scouter.agent.proxy.IHttpTrace; import scouter.lang.pack.XLogDiscardTypes; import scouter.lang.step.ApiCallStep; import scouter.lang.step.DumpStep; import scouter.lang.step.SqlStep; import scouter.lang.step.ThreadCallPossibleStep; import scouter.util.IntKeyMap; +import scouter.util.LongKeyLinkedMap; import scouter.util.SysJMX; import java.util.ArrayList; @@ -30,10 +32,34 @@ import java.util.concurrent.LinkedBlockingQueue; public class TraceContext { + public static class TimedScannable { + public long start; + public Object scannable; + + public TimedScannable(long start, Object scannable) { + this.start = start; + this.scannable = scannable; + } + } + public enum GetBy { + ThreadLocal, + ThreadLocalTxid, + ThreadLocalTxidByCoroutine, + CoroutineLocal + } + public GetBy getBy; + public LongKeyLinkedMap scannables; + private boolean isSummary; public boolean isStaticContents; public boolean isFullyDiscardService; + public boolean isReactiveStarted; + public boolean isReactiveTxidMarked; + public long exchangeHashCode; + public boolean isCoroutineStarted; + public boolean isOnCoroutineIdUpdating; + protected TraceContext() { } @@ -46,6 +72,15 @@ public TraceContext(boolean profile_summary) { } } + public void initScannables() { + scannables = new LongKeyLinkedMap(); + scannables.setMax(10000); + } + + public Object req; + public Object res; + public IHttpTrace http; + public TraceContext parent; public long txid; public Thread thread; @@ -164,6 +199,11 @@ public TraceContext(boolean profile_summary) { public ArrayList plcGroupList = new ArrayList(); public TraceContext createChild() { TraceContext child = new TraceContext(this.isSummary); + if (this.isReactiveStarted) { + child.initScannables(); + child.isReactiveStarted = true; + child.exchangeHashCode = this.exchangeHashCode; + } child.parent = this; child.txid = this.txid; child.thread = this.thread; diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContextManager.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContextManager.java index 23de5985f..5dd4a901a 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContextManager.java +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceContextManager.java @@ -19,29 +19,43 @@ import scouter.agent.Configure; import scouter.util.KeyGen; +import scouter.util.LongKeyLinkedMap; import scouter.util.LongKeyMap; +import scouter.util.LongLongLinkedMap; import java.util.Enumeration; +import static scouter.agent.trace.TraceContext.GetBy.CoroutineLocal; +import static scouter.agent.trace.TraceContext.GetBy.ThreadLocal; +import static scouter.agent.trace.TraceContext.GetBy.ThreadLocalTxid; +import static scouter.agent.trace.TraceContext.GetBy.ThreadLocalTxidByCoroutine; + public class TraceContextManager { private static Configure conf = Configure.getInstance(); - private static LongKeyMap entry = new LongKeyMap(); - private static ThreadLocal local = new ThreadLocal(); + private static LongKeyMap entryByThreadId = new LongKeyMap(); + private static LongKeyLinkedMap entryByTxid = new LongKeyLinkedMap().setMax(10000); + + private static final ThreadLocal local = new ThreadLocal(); + private static final ThreadLocal txidLocal = new ThreadLocal(); + public static final ThreadLocal txidByCoroutine = new ThreadLocal(); + + private static CoroutineDebuggingLocal coroutineDebuggingLocal = new CoroutineDebuggingLocal(); + private static LongKeyMap deferredEntry = new LongKeyMap(); //pass = 1, discard = 2, end-processing-with-path = -1, end-processing-with-path = -2 private static ThreadLocal forceDiscard = new ThreadLocal(); public static int size() { - return entry.size(); + return entryByTxid.size(); } public static int[] getActiveCount() { int[] act = new int[3]; try { long now = System.currentTimeMillis(); - Enumeration en = entry.values(); + Enumeration en = entryByTxid.values(); while (en.hasMoreElements()) { TraceContext ctx = en.nextElement(); long tm = now - ctx.startTime; @@ -53,42 +67,77 @@ public static int[] getActiveCount() { act[2]++; } } - - Enumeration enDeferred = deferredEntry.values(); - while (enDeferred.hasMoreElements()) { - TraceContext ctx = enDeferred.nextElement(); - long tm = now - ctx.startTime; - if (tm < conf.trace_activeserivce_yellow_time) { - act[0]++; - } else if (tm < conf.trace_activeservice_red_time) { - act[1]++; - } else { - act[2]++; - } - } } catch (Throwable t) { } return act; } public static Enumeration getContextEnumeration() { - return entry.values(); + return entryByTxid.values(); + } + + public static Enumeration getThreadingContextEnumeration() { + return entryByThreadId.values(); } public static Enumeration getDeferredContextEnumeration() { return deferredEntry.values(); } - public static TraceContext getContext(long key) { - return entry.get(key); + public static TraceContext getContext() { + Long txid = txidLocal.get(); + TraceContext traceContext = txid == null ? null : entryByTxid.get(txid); + + if (traceContext != null) { + traceContext.getBy = ThreadLocalTxid; + return traceContext; + } + + txid = txidByCoroutine.get(); + traceContext = txid == null ? null : entryByTxid.get(txid); + + if (traceContext != null) { + traceContext.getBy = ThreadLocalTxidByCoroutine; + return traceContext; + } + + traceContext = local.get(); + if (traceContext != null) { + traceContext.getBy = ThreadLocal; + return traceContext; + } + + traceContext = getCoroutineContext(); + if (traceContext != null) { + traceContext.getBy = CoroutineLocal; + return traceContext; + } + + return null; + } + + public static TraceContext getContextByTxid(long txid) { + return entryByTxid.get(txid); + } + + public static TraceContext getContextByThreadId(long key) { + return entryByThreadId.get(key); } public static TraceContext getDeferredContext(long key) { return deferredEntry.get(key); } - public static TraceContext getContext() { - return local.get(); + public static TraceContext getCoroutineContext() { + return coroutineDebuggingLocal.get(); + } + + public static TraceContext getCoroutineContext(long id) { + return coroutineDebuggingLocal.get(id); + } + + public static Long getLocalTxid() { + return txidLocal.get(); } public static void clearForceDiscard() { @@ -145,18 +194,63 @@ public static boolean startForceDiscard() { return discard; } - - public static long start(Thread thread, TraceContext o) { - long key = thread.getId(); + public static void start(TraceContext o) { local.set(o); - entry.put(key, o); - return key; + txidLocal.set(o.txid); + entryByTxid.put(o.txid, o); + + if (!o.isReactiveStarted) { + entryByThreadId.put(o.threadId, o); + } + } + + public static void takeoverTxid(TraceContext o, long oldTxid) { + txidLocal.set(o.txid); + entryByTxid.remove(oldTxid); + entryByTxid.put(o.txid, o); + } + + public static void startByCoroutine(TraceContext o) { + txidByCoroutine.set(o.txid); + } + + public static void end(TraceContext o) { + clearAllContext(o); + } + + private static LongLongLinkedMap threadTxidMap = new LongLongLinkedMap().setMax(2000); + private static LongLongLinkedMap txidThreadMap = new LongLongLinkedMap().setMax(2000); + + private static LongKeyMap map = new LongKeyMap(); + + public static void setTxidLocal(Long txid) { + txidLocal.set(txid); + if (txid != null && conf._psts_enabled && conf._psts_progressive_reactor_thread_trace_enabled) { + long threadId = Thread.currentThread().getId(); + txidThreadMap.put(txid, threadId); + threadTxidMap.put(threadId, txid); + } + } + + public static long getReactiveThreadId(long txid) { + if (!conf._psts_progressive_reactor_thread_trace_enabled) { + return 0; + } + long threadId = txidThreadMap.get(txid); + if (threadId == 0) { + return 0; + } + long txid0 = threadTxidMap.get(threadId); + if (txid0 == txid) { + return threadId; + } + return 0; } - public static void end(long key) { + public static void asCoroutineDebuggingMode(Long coroutineId, TraceContext o) { + CoroutineDebuggingLocal.setCoroutineDebuggingId(coroutineId); + coroutineDebuggingLocal.put(o); local.set(null); - entry.remove(key); - clearForceDiscard(); } public static void toDeferred(TraceContext o) { @@ -166,4 +260,22 @@ public static void toDeferred(TraceContext o) { public static void completeDeferred(TraceContext o) { deferredEntry.remove(o.txid); } -} \ No newline at end of file + + public static void clearAllContext(TraceContext o) { + local.set(null); + coroutineDebuggingLocal.clear(); //it should be prev of txidLocal clear + + entryByTxid.remove(o.txid); + if (conf._psts_progressive_reactor_thread_trace_enabled) { + txidThreadMap.remove(o.txid); + } + + txidByCoroutine.set(null); + if (!o.isReactiveStarted) { //do not clear txidLocal in reactive + txidLocal.set(null); + entryByThreadId.remove(o.threadId); + } + + clearForceDiscard(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceElasticSearch.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceElasticSearch.java new file mode 100644 index 000000000..6afc510ea --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceElasticSearch.java @@ -0,0 +1,132 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.agent.trace; + +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.counter.meter.MeterInteraction; +import scouter.agent.counter.meter.MeterInteractionManager; +import scouter.agent.netio.data.DataProxy; +import scouter.agent.proxy.ElasticSearchTraceFactory; +import scouter.agent.proxy.IElasticSearchTracer; +import scouter.lang.enumeration.ParameterizedMessageLevel; +import scouter.lang.step.ParameterizedMessageStep; +import scouter.util.StringUtil; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/16 + */ +public class TraceElasticSearch { + + private static String ES_COMMAND_MSG = "[ElasticSearch] %s"; + private static String ES_COMMAND_ERROR_MSG = "[ElasticSearch] %s\n[Exception:%s] %s"; + + static IElasticSearchTracer tracer; + static Configure conf = Configure.getInstance(); + + public static void startRequest(Object httpRequestBase) { + TraceContext ctx = TraceContextManager.getContext(); + if (ctx == null) { + return; + } + if (tracer == null) { + tracer = ElasticSearchTraceFactory.create(httpRequestBase.getClass().getClassLoader()); + } + + try { + String esRequestDesc = tracer.getRequestDescription(ctx, httpRequestBase); + + ParameterizedMessageStep step = new ParameterizedMessageStep(); + step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); + step.putTempMessage("desc", esRequestDesc); + ctx.profile.add(step); + StepTransferMap.put(System.identityHashCode(httpRequestBase), ctx, step); + } catch (Throwable e) { + Logger.println("ES001", e.getMessage(), e); + } + } + + public static void endRequest(Object httpUriRequest, Object httpHost, Object httpResponse) { + endRequestFinal(httpUriRequest, httpResponse, httpHost, null); + } + + public static void endFailRequest(Object httpUriRequest, Object node, Exception exception) { + endRequestFinal(httpUriRequest, null, node, exception); + } + + private static void endRequestFinal(Object httpRequestBase, Object httpResponseBase, Object hostOrNode, Throwable throwable) { + if (httpRequestBase == null) { + return; + } + + try { + int requestBaseHash = System.identityHashCode(httpRequestBase); + StepTransferMap.ID id = StepTransferMap.get(requestBaseHash); + if (id == null) { + return; + } + StepTransferMap.remove(requestBaseHash); + + TraceContext ctx = id.ctx; + ParameterizedMessageStep step = (ParameterizedMessageStep) id.step; + if (ctx == null || step == null) return; + + if (tracer == null) { + tracer = ElasticSearchTraceFactory.create(httpRequestBase.getClass().getClassLoader()); + } + if (throwable == null && httpResponseBase != null) { + throwable = tracer.getResponseError(httpRequestBase, httpResponseBase); + } + + int elapsed = (int) (System.currentTimeMillis() - ctx.startTime) - step.start_time; + step.setElapsed(elapsed); + + String desc = step.getTempMessage("desc"); + + if (StringUtil.isEmpty(desc)) desc = "-"; + + if (throwable == null) { + step.setMessage(DataProxy.sendHashedMessage(ES_COMMAND_MSG), desc); + step.setLevel(ParameterizedMessageLevel.INFO); + + } else { + String msg = throwable.toString(); + step.setMessage(DataProxy.sendHashedMessage(ES_COMMAND_ERROR_MSG), desc, throwable.getClass().getName(), msg); + step.setLevel(ParameterizedMessageLevel.ERROR); + + if (ctx.error == 0 && conf.xlog_error_on_elasticsearch_exception_enabled) { + ctx.error = DataProxy.sendError(msg); + } + //TODO not yet error summary processing for es : ctx.offerErrorEntity(ErrorEntity.of(throwable, ctx.error, 0, 0)); + } +// ctx.profile.pop(step); + + if (conf.counter_interaction_enabled) { + String node = tracer.getNode(ctx, hostOrNode); + int nodeHash = DataProxy.sendObjName(node); + MeterInteraction meterInteraction = MeterInteractionManager.getInstance().getElasticSearchCallMeter(conf.getObjHash(), nodeHash); + if (meterInteraction != null) { + meterInteraction.add(elapsed, throwable != null); + } + } + } catch (Throwable e) { + Logger.println("ES002", e.getMessage(), e); + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMain.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMain.java index c666b8431..7c01d6ff2 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMain.java +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMain.java @@ -29,6 +29,7 @@ import scouter.agent.error.STATEMENT_LEAK_SUSPECT; import scouter.agent.error.USERTX_NOT_CLOSE; import scouter.agent.netio.data.DataProxy; +import scouter.agent.plugin.AbstractPlugin; import scouter.agent.plugin.PluginAppServiceTrace; import scouter.agent.plugin.PluginBackThreadTrace; import scouter.agent.plugin.PluginCaptureTrace; @@ -38,8 +39,10 @@ import scouter.agent.proxy.IHttpTrace; import scouter.agent.proxy.IKafkaTracer; import scouter.agent.proxy.ILettuceTrace; +import scouter.agent.proxy.IReactiveSupport; import scouter.agent.proxy.KafkaTraceFactory; import scouter.agent.proxy.LettuceTraceFactory; +import scouter.agent.proxy.ReactiveSupportFactory; import scouter.agent.summary.ServiceSummary; import scouter.agent.wrapper.async.WrTask; import scouter.agent.wrapper.async.WrTaskCallable; @@ -79,6 +82,9 @@ import static scouter.lang.pack.XLogDiscardTypes.XLogDiscard; public class TraceMain { + + public static final String[] EMPTY_PARAM = new String[0]; + public static class Stat { public TraceContext ctx; public Object req; @@ -96,7 +102,10 @@ public Stat(TraceContext ctx) { } } - private static IHttpTrace http = null; + public static IHttpTrace http = null; + public static IHttpTrace reactiveHttp = null; + public static IReactiveSupport reactiveSupport = null; + private static Configure conf = Configure.getInstance(); private static Error REJECT = new REQUEST_REJECT("service rejected"); private static Error userTxNotClose = new USERTX_NOT_CLOSE("UserTransaction missing commit/rollback Error"); @@ -137,6 +146,52 @@ public static Object startHttpFilter(Object req, Object res) { return null; } + public static void startReactiveInit(Object obj) { + } + + public static void startReactiveHttpService(Object exchange) { + try { + if (reactiveSupport == null) { + initReactiveSupport(exchange); + } + } catch (Throwable t) { + Logger.println("A143", "fail to deploy ", t); + } + try { + Object req = AbstractPlugin.invokeMethod(exchange, "getRequest"); + Object res = AbstractPlugin.invokeMethod(exchange, "getResponse"); + + TraceContext ctx = TraceContextManager.getContext(); + if (ctx != null && ctx.exchangeHashCode != exchange.hashCode()) { + //Logger.trace("exchange hash is different on context : " + exchange.hashCode() + " : " + ctx.exchangeHashCode); + ctx = null; + } + if (ctx != null) { + return; + } + if (TraceContextManager.startForceDiscard()) { + return; + } + startReactiveHttp(req, res, exchange); + } catch (Throwable t) { + Logger.println("A143", "fail to deploy ", t); + } + } + + public static Object startReactiveHttpServiceReturn(Object mono) { + TraceContext ctx = TraceContextManager.getContext(); + if (ctx == null) { + return mono; + } + if (!ctx.isReactiveStarted) { + return mono; + } + if (reactiveSupport == null) { + return mono; + } + return reactiveSupport.subscriptOnContext(mono, ctx); + } + public static Object reject(Object stat, Object req, Object res) { Configure conf = Configure.getInstance(); if (plController != null) { @@ -189,7 +244,7 @@ private static void addHttpServiceName(TraceContext ctx, Object req) { StringBuilder sb = new StringBuilder(); if (conf.trace_service_name_post_key != null) { - String v = http.getParameter(req, conf.trace_service_name_post_key); + String v = ctx.http.getParameter(req, conf.trace_service_name_post_key); if (v != null) { if (sb.length() == 0) { sb.append(ctx.serviceName); @@ -218,7 +273,7 @@ private static void addHttpServiceName(TraceContext ctx, Object req) { } } if (conf.trace_service_name_header_key != null) { - String v = http.getHeader(req, conf.trace_service_name_header_key); + String v = ctx.http.getHeader(req, conf.trace_service_name_header_key); ctx.serviceName = new StringBuilder(ctx.serviceName.length() + v.length() + 5).append(ctx.serviceName) .append('-').append(v).toString(); } @@ -231,14 +286,30 @@ private static void addHttpServiceName(TraceContext ctx, Object req) { private static Object lock = new Object(); + private static Object startReactiveHttp(Object req, Object res, Object exchange) { + if (reactiveHttp == null) { + initReactiveHttp(req); + } + return startHttp(req, res, reactiveHttp, true, exchange); + } + private static Object startHttp(Object req, Object res) { if (http == null) { initHttp(req); } + return startHttp(req, res, http, false, null); + } + private static Object startHttp(Object req, Object res, IHttpTrace http0, boolean isReactive, Object exchange) { Configure conf = Configure.getInstance(); TraceContext ctx = new TraceContext(false); + if (isReactive) { + ctx.initScannables(); + ctx.isReactiveStarted = true; + ctx.exchangeHashCode = exchange.hashCode(); + } ctx.thread = Thread.currentThread(); + ctx.threadId = ctx.thread.getId(); ctx.txid = KeyGen.next(); ctx.startTime = System.currentTimeMillis(); ctx.startCpu = SysJMX.getCurrentThreadCPU(); @@ -251,7 +322,10 @@ private static Object startHttp(Object req, Object res) { step.hash = DataProxy.sendHashedMessage("[driving thread] " + ctx.threadName); ctx.profile.add(step); - http.start(ctx, req, res); + http0.start(ctx, req, res); + ctx.req = req; + ctx.res = res; + ctx.http = http0; if (ctx.isFullyDiscardService) { return null; @@ -261,14 +335,14 @@ private static Object startHttp(Object req, Object res) { ctx.serviceName = "Non-URI"; } - ctx.threadId = TraceContextManager.start(ctx.thread, ctx); + TraceContextManager.start(ctx); Stat stat = new Stat(ctx, req, res); stat.isStaticContents = ctx.isStaticContents; if (stat.isStaticContents == false) { if (ctx.xType != XLogTypes.ASYNCSERVLET_DISPATCHED_SERVICE) { - PluginHttpServiceTrace.start(ctx, req, res); + PluginHttpServiceTrace.start(ctx, req, res, http0, isReactive); } if (plController != null) { @@ -286,6 +360,46 @@ private static void initHttp(Object req) { } } + private static void initReactiveHttp(Object req) { + synchronized (lock) { + if (reactiveHttp == null) { + reactiveHttp = HttpTraceFactory.create(req.getClass().getClassLoader(), req); + } + } + } + + private static void initReactiveSupport(Object obj) { + synchronized (lock) { + if (reactiveSupport == null) { + reactiveSupport = ReactiveSupportFactory.create(obj.getClass().getClassLoader()); + reactiveSupport.contextOperatorHook(); + } + } + } + + public static void endReactiveHttpService() { + TraceContext context = TraceContextManager.getContext(); + if (context == null) { + return; + } + Stat stat = new Stat(context, context.req, context.res); + endHttpService(stat, null); + } + + public static void endCanceledHttpService(TraceContext traceContext) { + if (traceContext != null && !traceContext.endHttpProcessingStarted) { +// traceContext.error += 1; + + ParameterizedMessageStep step = new ParameterizedMessageStep(); + step.setMessage(DataProxy.sendHashedMessage("reactive stream canceled finish"), EMPTY_PARAM); + step.setLevel(ParameterizedMessageLevel.INFO); + step.start_time = (int) (System.currentTimeMillis() - traceContext.startTime); + traceContext.profile.add(step); + + endHttpService(new Stat(traceContext), null); + } + } + public static void endHttpService(Object stat, Throwable thr) { if (TraceContextManager.isForceDiscarded()) { TraceContextManager.clearForceDiscard(); @@ -298,10 +412,13 @@ public static void endHttpService(Object stat, Throwable thr) { return; } TraceContext ctx = stat0.ctx; + if (ctx == null) { + return; + } //wait on async servlet completion if (!ctx.asyncServletStarted) { - endHttpServiceFinal(ctx, stat0.req, stat0.res, thr); + endHttpServiceFinal(ctx, ctx.req, ctx.res, thr); } else { HashedMessageStep step = new HashedMessageStep(); step.time = -1; @@ -309,7 +426,7 @@ public static void endHttpService(Object stat, Throwable thr) { step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); ctx.profile.add(step); flushErrorSummary(ctx); - TraceContextManager.end(ctx.threadId); + TraceContextManager.end(ctx); ctx.latestCpu = SysJMX.getCurrentThreadCPU(); ctx.latestBytes = SysJMX.getCurrentThreadAllocBytes(conf.profile_thread_memory_usage_enabled); TraceContextManager.toDeferred(ctx); @@ -328,7 +445,7 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object //prevent duplication invoke synchronized (ctx) { if (ctx.endHttpProcessingStarted) { - Logger.println("[warn] duplicated endHttpServiceFinal() called !!! : " + ctx.serviceName); + Logger.println("[info] duplicated endHttpServiceFinal() called.: " + ctx.serviceName); return; } ctx.endHttpProcessingStarted = true; @@ -336,7 +453,7 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object try { if (conf.getEndUserPerfEndpointHash() == ctx.serviceHash) { - TraceContextManager.end(ctx.threadId); + TraceContextManager.end(ctx); return; } //additional service name @@ -344,15 +461,15 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object // add error summary flushErrorSummary(ctx); // HTTP END - http.end(ctx, request, response); + ctx.http.end(ctx, request, response); // static-contents -> stop processing if (ctx.isStaticContents) { - TraceContextManager.end(ctx.threadId); + TraceContextManager.end(ctx); return; } // Plug-in end if (ctx.xType != XLogTypes.ASYNCSERVLET_DISPATCHED_SERVICE) { - PluginHttpServiceTrace.end(ctx, request, response); + PluginHttpServiceTrace.end(ctx, request, response, ctx.http, ctx.isReactiveStarted); } if (plController != null) { plController.end(ctx, request, response); @@ -396,7 +513,7 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object } // profile close - TraceContextManager.end(ctx.threadId); + TraceContextManager.end(ctx); Configure conf = Configure.getInstance(); XLogPack pack = new XLogPack(); @@ -422,27 +539,46 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object } else { pack.hasDump = 0; } - // //////////////////////////////////////////////////////// if (ctx.error != 0) { pack.error = ctx.error; + } else if (thr != null) { if (thr == REJECT) { Logger.println("A145", ctx.serviceName); String emsg = conf.control_reject_text; pack.error = DataProxy.sendError(emsg); ServiceSummary.getInstance().process(thr, pack.error, ctx.serviceHash, ctx.txid, 0, 0); + } else { String emsg = thr.toString(); if (conf.profile_fullstack_service_error_enabled) { StringBuffer sb = new StringBuffer(); sb.append(emsg).append("\n"); ThreadUtil.getStackTrace(sb, thr, conf.profile_fullstack_max_lines); + Throwable[] suppressed = thr.getSuppressed(); + if (suppressed != null) { + for (Throwable sup : suppressed) { + sb.append("\nSuppressed...\n"); + sb.append(sup.toString()).append("\n"); + ThreadUtil.getStackTrace(sb, sup, conf.profile_fullstack_max_lines); + } + } + Throwable thrCause = thr.getCause(); if (thrCause != null) { thr = thrCause; while (thr != null) { sb.append("\nCause...\n"); ThreadUtil.getStackTrace(sb, thr, conf.profile_fullstack_max_lines); + Throwable[] suppressed2 = thr.getSuppressed(); + if (suppressed2 != null) { + for (Throwable sup : suppressed2) { + sb.append("\nSuppressed...\n"); + sb.append(sup.toString()).append("\n"); + ThreadUtil.getStackTrace(sb, sup, conf.profile_fullstack_max_lines); + } + } + thr = thr.getCause(); } } @@ -467,7 +603,7 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object pack.discardType = discardMode.byteFlag; ctx.discardType = discardMode; - ctx.profile.close(discardMode == XLogDiscard.NONE || !pack.isDriving()); + ctx.profile.close(discardMode == XLogDiscard.NONE || (!pack.isDriving() && !discardMode.isForceDiscard())); pack.profileSize = ctx.profileSize; pack.profileCount = ctx.profileCount; @@ -511,16 +647,19 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object meteringInteraction(ctx, pack); //send all child xlogs, and check it again on the collector server. (follows parent's discard type) - if (discardMode != XLogDiscard.DISCARD_ALL || !pack.isDriving()) { - if (ctx.latestCpu > 0) { - pack.cpu = (int) (ctx.latestCpu - ctx.startCpu); - } else { - pack.cpu = (int) (SysJMX.getCurrentThreadCPU() - ctx.startCpu); - } - if (ctx.latestBytes > 0) { - pack.kbytes = (int) ((ctx.latestBytes - ctx.bytes) / 1024.0d); - } else { - pack.kbytes = (int) ((SysJMX.getCurrentThreadAllocBytes(conf.profile_thread_memory_usage_enabled) - ctx.bytes) / 1024.0d); + if ((discardMode != XLogDiscard.DISCARD_ALL && discardMode != XLogDiscard.DISCARD_ALL_FORCE) + || (!pack.isDriving() && discardMode == XLogDiscard.DISCARD_ALL)) { + if (!ctx.isReactiveStarted) { + if (ctx.latestCpu > 0) { + pack.cpu = (int) (ctx.latestCpu - ctx.startCpu); + } else { + pack.cpu = (int) (SysJMX.getCurrentThreadCPU() - ctx.startCpu); + } + if (ctx.latestBytes > 0) { + pack.kbytes = (int) ((ctx.latestBytes - ctx.bytes) / 1024.0d); + } else { + pack.kbytes = (int) ((SysJMX.getCurrentThreadAllocBytes(conf.profile_thread_memory_usage_enabled) - ctx.bytes) / 1024.0d); + } } DataProxy.sendXLog(pack); @@ -528,7 +667,7 @@ public static void endHttpServiceFinal(TraceContext ctx, Object request, Object DataProxy.sendDroppedXLog(new DroppedXLogPack(pack.gxid, pack.txid)); } } catch (Throwable e) { - Logger.println("A146", e); + Logger.println("A146", e.getMessage(), e); } } @@ -621,7 +760,11 @@ public static Object startService(String name, String className, String methodNa ctx.startTime = System.currentTimeMillis(); ctx.startCpu = SysJMX.getCurrentThreadCPU(); ctx.txid = KeyGen.next(); - ctx.threadId = TraceContextManager.start(ctx.thread, ctx); + ctx.thread = Thread.currentThread(); + ctx.threadId = ctx.thread.getId(); + + TraceContextManager.start(ctx); + ctx.bytes = SysJMX.getCurrentThreadAllocBytes(conf.profile_thread_memory_usage_enabled); ctx.profile_thread_cputime = conf.profile_thread_cputime_enabled; ctx.xType = xType; @@ -679,7 +822,7 @@ public static void endService(Object stat, Object returnValue, Throwable thr) { } step.error = errorCheck(ctx, thr); ctx.profile.pop(step); - TraceContextManager.end(ctx.threadId); + TraceContextManager.end(ctx); ctx.profile.close(true); return; } @@ -690,7 +833,7 @@ public static void endService(Object stat, Object returnValue, Throwable thr) { PluginBackThreadTrace.end(ctx); } - TraceContextManager.end(ctx.threadId); + TraceContextManager.end(ctx); XLogPack pack = new XLogPack(); pack.txid = ctx.txid; @@ -705,7 +848,7 @@ public static void endService(Object stat, Object returnValue, Throwable thr) { XLogDiscard discardMode = findXLogDiscard(ctx, conf, pack); pack.discardType = discardMode.byteFlag; ctx.discardType = discardMode; - ctx.profile.close(discardMode == XLogDiscard.NONE || !pack.isDriving()); + ctx.profile.close(discardMode == XLogDiscard.NONE || (!pack.isDriving() && !discardMode.isForceDiscard())); pack.profileCount = ctx.profileCount; pack.profileSize = ctx.profileSize; @@ -751,7 +894,9 @@ public static void endService(Object stat, Object returnValue, Throwable thr) { meteringInteraction(ctx, pack); //send all child xlogs, and check it again on the collector server. (follows parent's discard type) - if (discardMode != XLogDiscard.DISCARD_ALL || !pack.isDriving()) { + if ((discardMode != XLogDiscard.DISCARD_ALL && discardMode != XLogDiscard.DISCARD_ALL_FORCE) + || (!pack.isDriving() && discardMode == XLogDiscard.DISCARD_ALL)) { + pack.cpu = (int) (SysJMX.getCurrentThreadCPU() - ctx.startCpu); pack.kbytes = (int) ((SysJMX.getCurrentThreadAllocBytes(conf.profile_thread_memory_usage_enabled) - ctx.bytes) / 1024.0d); DataProxy.sendXLog(pack); @@ -1025,7 +1170,7 @@ private static XLogDiscard findXLogDiscard(TraceContext ctx, Configure conf, XLo XLogDiscard discardMode = pack.error != 0 ? XLogDiscard.NONE : XLogSampler.getInstance().evaluateXLogDiscard(pack.elapsed, ctx.serviceName); //check xlog discard pattern if (XLogSampler.getInstance().isDiscardServicePattern(ctx.serviceName)) { - discardMode = XLogDiscard.DISCARD_ALL; + discardMode = XLogDiscard.DISCARD_ALL_FORCE; if (pack.error != 0 && conf.xlog_discard_service_show_error) { discardMode = XLogDiscard.NONE; } @@ -1071,9 +1216,9 @@ public static void addMessage(String msg) { } public static void endRequestAsyncStart(Object asyncContext) { - if (http == null) return; TraceContext traceContext = TraceContextManager.getContext(); if (traceContext == null) return; + if (http == null) return; http.addAsyncContextListener(asyncContext); traceContext.asyncServletStarted = true; } @@ -1180,7 +1325,11 @@ public static Object startAsyncPossibleService(Object keyObject, String fullName } localContext.service = true; if (id.gxid != 0) localContext.context.gxid = id.gxid; - if (id.callee != 0) localContext.context.txid = id.callee; + if (id.callee != 0) { + long oldTxid = localContext.context.txid; + localContext.context.txid = id.callee; + TraceContextManager.takeoverTxid(localContext.context, oldTxid); + } if (id.caller != 0) localContext.context.caller = id.caller; String serviceName = StringUtil.removeLastString(className, '/') + "#" + methodName + "() -- " + fullName; @@ -1350,7 +1499,11 @@ public static Object callRunnableCallInvoked(Object callRunnableObj) { } localContext.service = true; if (id.gxid != 0) localContext.context.gxid = id.gxid; - if (id.callee != 0) localContext.context.txid = id.callee; + if (id.callee != 0) { + long oldTxid = localContext.context.txid; + localContext.context.txid = id.callee; + TraceContextManager.takeoverTxid(localContext.context, oldTxid); + } if (id.caller != 0) localContext.context.caller = id.caller; return localContext; @@ -1538,10 +1691,26 @@ public static void startExceptionHandler(String className, String methodName, St if (conf.profile_fullstack_hooked_exception_enabled) { sb.append("\n"); ThreadUtil.getStackTrace(sb, t, conf.profile_fullstack_max_lines); + Throwable[] suppressed = t.getSuppressed(); + if (suppressed != null) { + for (Throwable sup : suppressed) { + sb.append("\nSuppressed...\n"); + sb.append(sup.toString()).append("\n"); + ThreadUtil.getStackTrace(sb, sup, conf.profile_fullstack_max_lines); + } + } Throwable cause = t.getCause(); while (cause != null) { sb.append("\nCause...\n"); ThreadUtil.getStackTrace(sb, cause, conf.profile_fullstack_max_lines); + Throwable[] suppressed2 = t.getSuppressed(); + if (suppressed2 != null) { + for (Throwable sup : suppressed2) { + sb.append("\nSuppressed...\n"); + sb.append(sup.toString()).append("\n"); + ThreadUtil.getStackTrace(sb, sup, conf.profile_fullstack_max_lines); + } + } cause = cause.getCause(); } } diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMongoDB.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMongoDB.java new file mode 100644 index 000000000..7149d4e43 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceMongoDB.java @@ -0,0 +1,102 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.agent.trace; + +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.proxy.IMongoDbTracer; +import scouter.agent.proxy.MongoDbTraceFactory; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/16 + */ +public class TraceMongoDB { + + public static final String V405 = "v405"; + public static final String V382 = "v382"; + public static final String V364 = "v364"; + + static IMongoDbTracer tracer; + static Configure conf = Configure.getInstance(); + + public static Object startExecute(Object _this, Object connection, Object namespace, Object command, + Object readPreference, Object payload, String version) { + + TraceContext ctx = TraceContextManager.getContext(); + if (ctx == null) { + return null; + } + + try { + if (tracer == null) { + tracer = MongoDbTraceFactory.create(namespace.getClass().getClassLoader(), version); + } + StepTransferMap.ID id = tracer.generateAndTransferMongoQueryStep(ctx, _this, connection); + if (id == null) { + return null; + } + Object callback = tracer.genCallback(id, namespace, command, readPreference, payload); + if (callback == null) { + return null; + } + return callback; + + } catch (Throwable t) { + Logger.println("MTC01", t.getMessage(), t); + return null; + } + } + + public static void endExecute(Object callback, Throwable throwable) { + if (callback == null) { + return; + } + if (tracer == null) { + return; + } + try { + tracer.doCallback(callback, null, throwable); + + } catch (Throwable t) { + Logger.println("MTC02", t.getMessage(), t); + } + } + + public static Object startExecuteAsync(Object _this, Object connection, Object namespace, Object command, + Object readPreference, Object payload, Object callback, String version) { + TraceContext ctx = TraceContextManager.getContext(); + if (ctx == null) { + return callback; + } + try { + if (tracer == null) { + tracer = MongoDbTraceFactory.create(namespace.getClass().getClassLoader(), version); + } + StepTransferMap.ID id = tracer.generateAndTransferMongoQueryStep(ctx, _this, connection); + if (id == null) { + return callback; + } + return tracer.wrapCallback(id, namespace, command, readPreference, payload, callback); + + } catch (Throwable e) { + Logger.println("MTC03", e.getMessage(), e); + return callback; + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/TraceReactive.java b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceReactive.java new file mode 100644 index 000000000..47217055d --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/TraceReactive.java @@ -0,0 +1,116 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package scouter.agent.trace; + +import java.lang.reflect.Method; + +public class TraceReactive { + public static void threadSetName(Thread thread, String name) { + //System.out.println(">> Thread setname " + Thread.currentThread().getId() + " : " + Thread.currentThread().getName() + " -> " + name); + } + + public static void startCoroutineIdUpdateThreadContext(long coroutineId) { + TraceContext context = TraceContextManager.getContext(); + if (context == null) { + context = TraceContextManager.getCoroutineContext(coroutineId); + if (context == null) { + return; + } + } + + CoroutineDebuggingLocal.setCoroutineDebuggingId(coroutineId); + if (context.isReactiveStarted && !context.isCoroutineStarted) { + context.isCoroutineStarted = true; + context.isOnCoroutineIdUpdating = true; + TraceContextManager.asCoroutineDebuggingMode(coroutineId, context); + } + } + + public static void endCoroutineIdUpdateThreadContext() { + TraceContext context = TraceContextManager.getCoroutineContext(); + if (context == null) { + return; + } + context.isOnCoroutineIdUpdating = false; + } + + public static Object startMonoKtMono(Object coroutineContext) { + TraceContext context = TraceContextManager.getContext(); + if (context == null) { + return coroutineContext; + } + return TraceMain.reactiveSupport.monoCoroutineContextHook(coroutineContext, context); + } + + + + + + + + + + private static Object[] coroutineJobParams = null; + private static Method coroutineJobGetMethod = null; + private static Method jobIsActiveMethod = null; + + public static void startCoroutineIdRestoreThreadContext(Object coroutineContext) { + CoroutineDebuggingLocal.releaseCoroutineId(); + } +// public static void startCoroutineIdRestoreThreadContext(Object coroutineContext) { +// try { +// if (coroutineJobParams == null) { +// Class jobClass = Class.forName("kotlinx.coroutines.Job", false, Thread.currentThread().getContextClassLoader()); +// Field keyField = jobClass.getField("Key"); +// Object key = keyField.get(null); +// Object[] params = new Object[1]; +// params[0] = key; +// coroutineJobParams = params; +// } +// +// if (coroutineJobGetMethod == null) { +// Class[] typeParams = new Class[1]; +// Class arg = Class.forName("kotlin.coroutines.CoroutineContext$Key", false, Thread.currentThread().getContextClassLoader()); +// typeParams[0] = arg; +// Method method = coroutineContext.getClass().getMethod("get", typeParams[0]); +// coroutineJobGetMethod = method; +// } +// +// Object job = coroutineJobGetMethod.invoke(coroutineContext, coroutineJobParams); +// +// if (jobIsActiveMethod == null) { +// jobIsActiveMethod = job.getClass().getMethod("isActive"); +// } +// +// Object isActive = jobIsActiveMethod.invoke(job); +// if (isActive instanceof Boolean && !((Boolean) isActive)) { +// TraceContext context = TraceContextManager.getContext(); +// TraceMain.endCanceledHttpService(context); +// } +// //TODO +// /* +// Active Service 코루틴에 맞추기 +// child 코루틴과 연결 가능한지 테스트하기. +// */ +// +// } catch (Exception e) { +// Logger.println("A342", "reflection errors.", e); +// } +// +// CoroutineLocal.releaseCoroutineId(); +// } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/XLogSampler.java b/scouter.agent.java/src/main/java/scouter/agent/trace/XLogSampler.java index ae58f444e..514e7cec7 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/trace/XLogSampler.java +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/XLogSampler.java @@ -14,6 +14,8 @@ public class XLogSampler { private static XLogSampler instance = new XLogSampler(); private Configure conf; + private String currentExcludeSamplingPattern; + private String currentConsequentSamplingIgnorePattern; private String currentDiscardServicePatterns; private String currentSamplingServicePatterns; private String currentSampling2ServicePatterns; @@ -21,6 +23,8 @@ public class XLogSampler { private String currentSampling4ServicePatterns; private String currentSampling5ServicePatterns; private String currentFullyDiscardServicePatterns; + private CommaSeparatedChainedStrMatcher excludeSamplingPatternMatcher; + private CommaSeparatedChainedStrMatcher consequentSamplingIgnorePatternMatcher; private CommaSeparatedChainedStrMatcher discardPatternMatcher; private CommaSeparatedChainedStrMatcher samplingPatternMatcher; private CommaSeparatedChainedStrMatcher sampling2PatternMatcher; @@ -31,6 +35,8 @@ public class XLogSampler { private XLogSampler() { conf = Configure.getInstance(); + currentExcludeSamplingPattern = conf.xlog_sampling_exclude_patterns; + currentConsequentSamplingIgnorePattern = conf.xlog_consequent_sampling_ignore_patterns; currentDiscardServicePatterns = conf.xlog_discard_service_patterns; currentFullyDiscardServicePatterns = conf.xlog_fully_discard_service_patterns; currentSamplingServicePatterns = conf.xlog_patterned_sampling_service_patterns; @@ -38,6 +44,9 @@ private XLogSampler() { currentSampling3ServicePatterns = conf.xlog_patterned3_sampling_service_patterns; currentSampling4ServicePatterns = conf.xlog_patterned4_sampling_service_patterns; currentSampling5ServicePatterns = conf.xlog_patterned5_sampling_service_patterns; + + excludeSamplingPatternMatcher = new CommaSeparatedChainedStrMatcher(currentExcludeSamplingPattern); + consequentSamplingIgnorePatternMatcher = new CommaSeparatedChainedStrMatcher(currentConsequentSamplingIgnorePattern); discardPatternMatcher = new CommaSeparatedChainedStrMatcher(currentDiscardServicePatterns); fullyDiscardPatternMatcher = new CommaSeparatedChainedStrMatcher(currentFullyDiscardServicePatterns); samplingPatternMatcher = new CommaSeparatedChainedStrMatcher(currentSamplingServicePatterns); @@ -50,6 +59,14 @@ private XLogSampler() { @Override public void run() { XLogSampler sampler = XLogSampler.getInstance(); Configure conf = Configure.getInstance(); + if (sampler.currentExcludeSamplingPattern.equals(conf.xlog_sampling_exclude_patterns) == false) { + sampler.currentExcludeSamplingPattern = conf.xlog_sampling_exclude_patterns; + sampler.excludeSamplingPatternMatcher = new CommaSeparatedChainedStrMatcher(conf.xlog_sampling_exclude_patterns); + } + if (sampler.currentConsequentSamplingIgnorePattern.equals(conf.xlog_consequent_sampling_ignore_patterns) == false) { + sampler.currentConsequentSamplingIgnorePattern = conf.xlog_consequent_sampling_ignore_patterns; + sampler.consequentSamplingIgnorePatternMatcher = new CommaSeparatedChainedStrMatcher(conf.xlog_consequent_sampling_ignore_patterns); + } if (sampler.currentDiscardServicePatterns.equals(conf.xlog_discard_service_patterns) == false) { sampler.currentDiscardServicePatterns = conf.xlog_discard_service_patterns; sampler.discardPatternMatcher = new CommaSeparatedChainedStrMatcher(conf.xlog_discard_service_patterns); @@ -90,41 +107,53 @@ public XLogDiscard evaluateXLogDiscard(int elapsed, String serviceName) { XLogDiscard discardMode = XLogDiscard.NONE; if (elapsed < conf.xlog_lower_bound_time_ms) { - return XLogDiscard.DISCARD_ALL; + return XLogDiscard.DISCARD_ALL_FORCE; + } + + if (conf.xlog_sampling_enabled && isExcludeSamplingServicePattern(serviceName)) { + return XLogDiscard.NONE; } boolean isSamplingServicePattern = false; if (conf.xlog_patterned_sampling_enabled && (isSamplingServicePattern = isSamplingServicePattern(serviceName))) { - discardMode = samplingPatterned1(elapsed, discardMode); + discardMode = toForce(samplingPatterned1(elapsed, discardMode), serviceName); } if (!isSamplingServicePattern && conf.xlog_patterned2_sampling_enabled && (isSamplingServicePattern = isSampling2ServicePattern(serviceName))) { - discardMode = samplingPatterned2(elapsed, discardMode); + discardMode = toForce(samplingPatterned2(elapsed, discardMode), serviceName); } if (!isSamplingServicePattern && conf.xlog_patterned3_sampling_enabled && (isSamplingServicePattern = isSampling3ServicePattern(serviceName))) { - discardMode = samplingPatterned3(elapsed, discardMode); + discardMode = toForce(samplingPatterned3(elapsed, discardMode), serviceName); } if (!isSamplingServicePattern && conf.xlog_patterned4_sampling_enabled && (isSamplingServicePattern = isSampling4ServicePattern(serviceName))) { - discardMode = samplingPatterned4(elapsed, discardMode); + discardMode = toForce(samplingPatterned4(elapsed, discardMode), serviceName); } if (!isSamplingServicePattern && conf.xlog_patterned5_sampling_enabled && (isSamplingServicePattern = isSampling5ServicePattern(serviceName))) { - discardMode = samplingPatterned5(elapsed, discardMode); + discardMode = toForce(samplingPatterned5(elapsed, discardMode), serviceName); } if (!isSamplingServicePattern && conf.xlog_sampling_enabled) { - discardMode = sampling4Elapsed(elapsed, discardMode); + discardMode = toForce(sampling4Elapsed(elapsed, discardMode), serviceName); } return discardMode; } + private XLogDiscard toForce(XLogDiscard discardMode, String serviceName) { + if (isConsequentSamplingIgnoreServicePattern(serviceName)) { + return discardMode.toForce(); + } else { + return discardMode; + } + } + private XLogDiscard sampling4Elapsed(int elapsed, XLogDiscard discardMode) { if (elapsed < conf.xlog_sampling_step1_ms) { if (Math.abs(KeyGen.next() % 100) >= conf.xlog_sampling_step1_rate_pct) { @@ -251,6 +280,28 @@ private XLogDiscard samplingPatterned1(int elapsed, XLogDiscard discardMode) { return discardMode; } + private boolean isExcludeSamplingServicePattern(String serviceName) { + if (StringUtil.isEmpty(conf.xlog_sampling_exclude_patterns)) { + return false; + } + if (excludeSamplingPatternMatcher.isMatch(serviceName)) { + return true; + } else { + return false; + } + } + + private boolean isConsequentSamplingIgnoreServicePattern(String serviceName) { + if (StringUtil.isEmpty(conf.xlog_consequent_sampling_ignore_patterns)) { + return false; + } + if (consequentSamplingIgnorePatternMatcher.isMatch(serviceName)) { + return true; + } else { + return false; + } + } + public boolean isDiscardServicePattern(String serviceName) { if (StringUtil.isEmpty(conf.xlog_discard_service_patterns)) { return false; diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/api/ApiCallTraceHelper.java b/scouter.agent.java/src/main/java/scouter/agent/trace/api/ApiCallTraceHelper.java index 2cc3e5c64..6540f0e1a 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/trace/api/ApiCallTraceHelper.java +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/api/ApiCallTraceHelper.java @@ -18,6 +18,7 @@ package scouter.agent.trace.api; import scouter.agent.trace.HookArgs; +import scouter.agent.trace.LocalContext; import scouter.agent.trace.TraceContext; import scouter.lang.step.ApiCallStep; @@ -34,6 +35,7 @@ static interface IHelper { static ForHttpClient43 forHttpClient43 = new ForHttpClient43(); static ForSpringAsyncRestTemplate forSpringAsyncRestTemplate = new ForSpringAsyncRestTemplate(); static ForJavaNetHttpClient forJavaNetHttpClient = new ForJavaNetHttpClient(); + static ForWebClient forWebClient = new ForWebClient(); static void put(String name, IHelper o) { name = name.replace('.', '/'); @@ -48,14 +50,15 @@ public static IHelper get(String name) { put("sun/net/www/protocol/http/HttpURLConnection", new ForHttpURLConnection()); put("sun/net/www/http/HttpClient", new ForSunHttpClient()); put("org/apache/commons/httpclient/HttpClient", new ForHttpClient()); - put("org/apache/http/impl/client/InternalHttpClient", new ForHttpClient43()); + put("org/apache/http/impl/client/InternalHttpClient", forHttpClient43); put("org/apache/http/impl/client/AbstractHttpClient", new ForHttpClient40()); put("com/sap/mw/jco/JCO$Client", new ForJCOClient()); put("com/netflix/ribbon/transport/netty/http/LoadBalancingHttpClient", new ForRibbonLB()); put("io/reactivex/netty/protocol/http/client/HttpClientImpl", new ForNettyHttpRequest()); put("org/springframework/web/client/RestTemplate", new ForSpringRestTemplate()); - put("org/springframework/web/client/AsyncRestTemplate", new ForSpringAsyncRestTemplate()); - put("jdk/internal/net/http/HttpClientImpl", new ForJavaNetHttpClient()); + put("org/springframework/web/client/AsyncRestTemplate", forSpringAsyncRestTemplate); + put("jdk/internal/net/http/HttpClientImpl", forJavaNetHttpClient); + put("org/springframework/web/reactive/function/client/ExchangeFunctions$DefaultExchangeFunction", forWebClient); } private static IHelper defaultObj = new ForDefault(); @@ -82,7 +85,15 @@ public static void setCalleeToCtxInSpringClientHttpResponse(TraceContext ctx, Ob forSpringAsyncRestTemplate.processSetCalleeToCtx(ctx, _this, response); } - public static void setCalleeToCtxJavaHttpRequest(TraceContext ctx, Object requestBuilder) { + public static void setTransferToCtxJavaHttpRequest(TraceContext ctx, Object requestBuilder) { forJavaNetHttpClient.transfer(ctx, requestBuilder); } + + public static void webClientInfo(Object bodyInserterRequest, Object clientHttpRequest) { + forWebClient.processInfo(bodyInserterRequest, clientHttpRequest); + } + + public static LocalContext webClientProcessEnd(Object exchangeFunction, Object clientResponse) { + return forWebClient.processEnd(exchangeFunction, clientResponse); + } } diff --git a/scouter.agent.java/src/main/java/scouter/agent/trace/api/ForWebClient.java b/scouter.agent.java/src/main/java/scouter/agent/trace/api/ForWebClient.java new file mode 100644 index 000000000..ae4a33ebf --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/agent/trace/api/ForWebClient.java @@ -0,0 +1,154 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package scouter.agent.trace.api; + +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.plugin.PluginHttpCallTrace; +import scouter.agent.proxy.IHttpClient; +import scouter.agent.proxy.WebClientFactory; +import scouter.agent.trace.ApiCallTransferMap; +import scouter.agent.trace.HookArgs; +import scouter.agent.trace.LocalContext; +import scouter.agent.trace.TraceContext; +import scouter.lang.constants.B3Constant; +import scouter.lang.step.ApiCallStep; +import scouter.lang.step.ApiCallStep2; +import scouter.util.Hexa32; +import scouter.util.IntKeyLinkedMap; +import scouter.util.KeyGen; + +public class ForWebClient implements ApiCallTraceHelper.IHelper { + + private static IntKeyLinkedMap httpclients = new IntKeyLinkedMap().setMax(10); + private static Configure conf = Configure.getInstance(); + + public ApiCallStep process(TraceContext ctx, HookArgs hookPoint) { + ApiCallStep2 step = new ApiCallStep2(); + ctx.apicall_name = hookPoint.class1; + + if (ok) { + try { + if (hookPoint.args != null && hookPoint.args.length > 0) { + ApiCallTransferMap.put(System.identityHashCode(hookPoint.args[0]), ctx, step); + ApiCallTransferMap.put(System.identityHashCode(hookPoint.this1), ctx, step); + + } + } catch (Exception e) { + this.ok = false; + } + } + return step; + } + + public void processInfo(Object bodyInserter, Object clientHttpRequest) { + if (bodyInserter == null) { + return; + } + int bodyInserterHash = System.identityHashCode(bodyInserter); + ApiCallTransferMap.ID id = ApiCallTransferMap.get(bodyInserterHash); + if (id == null) { + return; + } + ApiCallTransferMap.remove(bodyInserterHash); + + TraceContext ctx = id.ctx; + ApiCallStep2 step = id.step; + if (ok) { + try { + IHttpClient httpclient = getProxy(clientHttpRequest); + String host = httpclient.getHost(clientHttpRequest); + + step.opt = 1; + step.address = host; + if (host != null) + ctx.apicall_target = host; + ctx.apicall_name = httpclient.getURI(clientHttpRequest); + + step.txid = KeyGen.next(); + transfer(httpclient, ctx, clientHttpRequest, step.txid); + } catch (Exception e) { + this.ok = false; + } + } + } + + public void processEnd(TraceContext ctx, ApiCallStep step, Object rtn, HookArgs hookPoint) { + //processEnd(rtn); + } + + public LocalContext processEnd(Object exchangeFunction, Object clientHttpResponse) { + int exchangeFunctionHash = System.identityHashCode(exchangeFunction); + ApiCallTransferMap.ID id = ApiCallTransferMap.get(exchangeFunctionHash); + if (id == null) { + return null; + } + ApiCallTransferMap.remove(exchangeFunctionHash); + + TraceContext ctx = id.ctx; + ApiCallStep2 step = id.step; + + IHttpClient httpclient = getProxy(exchangeFunction); + String calleeObjHashStr = httpclient.getResponseHeader(clientHttpResponse, conf._trace_interservice_callee_obj_header_key); + if (calleeObjHashStr != null) { + try { + ctx.lastCalleeObjHash = Integer.parseInt(calleeObjHashStr); + } catch (NumberFormatException e) { + } + } else { + ctx.lastCalleeObjHash = 0; + } + + return new LocalContext(ctx, step, httpclient.getResponseStatusCode(clientHttpResponse)); + } + + private IHttpClient getProxy(Object _this) { + int key = System.identityHashCode(_this.getClass()); + IHttpClient httpclient = httpclients.get(key); + if (httpclient == null) { + synchronized (this) { + httpclient = WebClientFactory.create(_this.getClass().getClassLoader()); + httpclients.put(key, httpclient); + } + } + return httpclient; + } + + private boolean ok = true; + + private void transfer(IHttpClient httpclient, TraceContext ctx, Object req, long calleeTxid) { + if (conf.trace_interservice_enabled) { + try { + if (ctx.gxid == 0) { + ctx.gxid = ctx.txid; + } + httpclient.addHeader(req, conf._trace_interservice_gxid_header_key, Hexa32.toString32(ctx.gxid)); + httpclient.addHeader(req, conf._trace_interservice_caller_header_key, Hexa32.toString32(ctx.txid)); + httpclient.addHeader(req, conf._trace_interservice_callee_header_key, Hexa32.toString32(calleeTxid)); + httpclient.addHeader(req, conf._trace_interservice_caller_obj_header_key, String.valueOf(conf.getObjHash())); + + httpclient.addHeader(req, B3Constant.B3_HEADER_TRACEID, Hexa32.toUnsignedLongHex(ctx.gxid)); + httpclient.addHeader(req, B3Constant.B3_HEADER_PARENTSPANID, Hexa32.toUnsignedLongHex(ctx.txid)); + httpclient.addHeader(req, B3Constant.B3_HEADER_SPANID, Hexa32.toUnsignedLongHex(calleeTxid)); + PluginHttpCallTrace.call(ctx, req); + } catch (Exception e) { + Logger.println("A178w", e); + ok = false; + } + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/agent/util/DumpUtil.java b/scouter.agent.java/src/main/java/scouter/agent/util/DumpUtil.java index 84440de6e..3aec4505e 100644 --- a/scouter.agent.java/src/main/java/scouter/agent/util/DumpUtil.java +++ b/scouter.agent.java/src/main/java/scouter/agent/util/DumpUtil.java @@ -108,7 +108,7 @@ public static Pack triggerThreadList() { out.print(stat.get(i) + ":"); out.print("cpu " + cpu.get(i)); - TraceContext ctx = TraceContextManager.getContext(tid); + TraceContext ctx = TraceContextManager.getContextByThreadId(tid); if (ctx != null) { out.print(":service " + Hexa32.toString32(ctx.txid) + ":"); out.print(ctx.serviceName + ":"); @@ -137,6 +137,7 @@ public static Pack triggerActiveService() { try { File file = DumpUtil.getDumpFile("scouter.activeservice"); out = new PrintWriter(new FileWriter(file)); + //TODO reactive support Enumeration en = TraceContextManager.getContextEnumeration(); for (int n = 0; en.hasMoreElements(); n++) { TraceContext ctx = en.nextElement(); diff --git a/scouter.agent.java/src/main/java/scouter/test/Service24H.java b/scouter.agent.java/src/main/java/scouter/test/Service24H.java index d5ca84d8b..508f6696f 100644 --- a/scouter.agent.java/src/main/java/scouter/test/Service24H.java +++ b/scouter.agent.java/src/main/java/scouter/test/Service24H.java @@ -17,9 +17,6 @@ package scouter.test; -import java.util.Random; -import java.util.Stack; - import scouter.AnyTrace; import scouter.agent.AgentBoot; import scouter.agent.Configure; @@ -38,6 +35,9 @@ import scouter.util.SysJMX; import scouter.util.ThreadUtil; +import java.util.Random; +import java.util.Stack; + public class Service24H { public static void main(String[] args) { @@ -135,17 +135,18 @@ private static void profile(long txid, int serviceHash) { ctx.txid=txid; ctx.serviceHash=serviceHash; ctx.startTime=System.currentTimeMillis(); + ctx.thread = Thread.currentThread(); - long key = TraceContextManager.start(Thread.currentThread(), ctx); + TraceContextManager.start(ctx); AnyTrace.message("profile 1"); AnyTrace.message("profile 2"); ctx.profile.close(true); - TraceContextManager.end(key); + TraceContextManager.end(ctx); } private static int next(Random r, int max) { return Math.abs(r.nextInt() % max); } -} \ No newline at end of file +} diff --git a/scouter.agent.java/src/main/java/scouter/test/TpsRush.java b/scouter.agent.java/src/main/java/scouter/test/TpsRush.java index c0c6a0854..a99f7a3bd 100644 --- a/scouter.agent.java/src/main/java/scouter/test/TpsRush.java +++ b/scouter.agent.java/src/main/java/scouter/test/TpsRush.java @@ -17,8 +17,6 @@ package scouter.test; -import java.util.Random; - import scouter.AnyTrace; import scouter.agent.AgentBoot; import scouter.agent.Configure; @@ -37,8 +35,10 @@ import scouter.util.SysJMX; import scouter.util.ThreadUtil; +import java.util.Random; + public class TpsRush { - public static void main(String[] args) { + public static void main2(String[] args) { ShellArg sh = new ShellArg(args); String server = sh.get("-h", "127.0.0.1"); @@ -115,17 +115,18 @@ private static void profile(long txid, int serviceHash) { ctx.txid=txid; ctx.serviceHash=serviceHash; ctx.startTime=System.currentTimeMillis(); + ctx.thread = Thread.currentThread(); - long key = TraceContextManager.start(Thread.currentThread(), ctx); + TraceContextManager.start(ctx); AnyTrace.message("profile 1"); AnyTrace.message("profile 2"); ctx.profile.close(true); - TraceContextManager.end(key); + TraceContextManager.end(ctx); } private static int next(Random r, int max) { return Math.abs(r.nextInt() % max); } -} \ No newline at end of file +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/http/HttpTrace.java b/scouter.agent.java/src/main/java/scouter/xtra/http/HttpTrace.java index 0107c5154..6bd2f2560 100644 --- a/scouter.agent.java/src/main/java/scouter/xtra/http/HttpTrace.java +++ b/scouter.agent.java/src/main/java/scouter/xtra/http/HttpTrace.java @@ -26,13 +26,22 @@ import scouter.agent.summary.EndUserErrorData; import scouter.agent.summary.EndUserNavigationData; import scouter.agent.summary.EndUserSummary; -import scouter.agent.trace.*; +import scouter.agent.trace.IProfileCollector; +import scouter.agent.trace.TraceContext; +import scouter.agent.trace.TraceContextManager; +import scouter.agent.trace.TraceMain; +import scouter.agent.trace.TransferMap; +import scouter.agent.trace.XLogSampler; import scouter.lang.conf.ConfObserver; import scouter.lang.constants.B3Constant; import scouter.lang.pack.XLogTypes; import scouter.lang.step.HashedMessageStep; import scouter.lang.step.MessageStep; -import scouter.util.*; +import scouter.util.CastUtil; +import scouter.util.CompareUtil; +import scouter.util.HashUtil; +import scouter.util.Hexa32; +import scouter.util.StringUtil; import scouter.util.zipkin.HexCodec; import javax.servlet.http.Cookie; @@ -42,7 +51,9 @@ import java.io.PrintWriter; import java.util.Enumeration; -import static scouter.agent.AgentCommonConstant.*; +import static scouter.agent.AgentCommonConstant.ASYNC_SERVLET_DISPATCHED_PREFIX; +import static scouter.agent.AgentCommonConstant.REQUEST_ATTRIBUTE_CALLER_TRANSFER_MAP; +import static scouter.agent.AgentCommonConstant.REQUEST_ATTRIBUTE_SELF_DISPATCHED; public class HttpTrace implements IHttpTrace { boolean remote_by_header; @@ -73,6 +84,48 @@ public void run() { }); } + private String getRequestURI(HttpServletRequest request) { + String uri = request.getRequestURI(); + if (uri == null) + return "no-url"; + int x = uri.indexOf(';'); + if (x > 0) + return uri.substring(0, x); + else + return uri; + } + + private String getRemoteAddr(HttpServletRequest request) { + try { + //For Testing + if (__ip_dummy_test) { + return getRandomIp(); + } + + if (remote_by_header) { + String remoteIp = request.getHeader(http_remote_ip_header_key); + if (remoteIp == null) { + return request.getRemoteAddr(); + } + + int commaPos = remoteIp.indexOf(','); + if (commaPos > -1) { + remoteIp = remoteIp.substring(0, commaPos); + } + + Logger.trace("remoteIp: " + remoteIp); + return remoteIp; + + } else { + return request.getRemoteAddr(); + } + + } catch (Throwable t) { + remote_by_header = false; + return "0.0.0.0"; + } + } + public String getParameter(Object req, String key) { HttpServletRequest request = (HttpServletRequest) req; @@ -88,6 +141,60 @@ public String getHeader(Object req, String key) { return request.getHeader(key); } + @Override + public String getCookie(Object req, String key) { + HttpServletRequest request = (HttpServletRequest) req; + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + if (cookie.getName().equals(key)) { + return cookie.getValue(); + } + } + return null; + } + + @Override + public String getRequestURI(Object req) { + HttpServletRequest request = (HttpServletRequest) req; + return getRequestURI(request); + } + + @Override + public String getRemoteAddr(Object req) { + HttpServletRequest request = (HttpServletRequest) req; + return getRemoteAddr(request); + } + + @Override + public String getMethod(Object req) { + HttpServletRequest request = (HttpServletRequest) req; + return request.getMethod(); + } + + @Override + public String getQueryString(Object req) { + HttpServletRequest request = (HttpServletRequest) req; + return request.getQueryString(); + } + + @Override + public Object getAttribute(Object req, String key) { + HttpServletRequest request = (HttpServletRequest) req; + return request.getAttribute(key); + } + + @Override + public Enumeration getParameterNames(Object req) { + HttpServletRequest request = (HttpServletRequest) req; + return request.getParameterNames(); + } + + @Override + public Enumeration getHeaderNames(Object req) { + HttpServletRequest request = (HttpServletRequest) req; + return request.getHeaderNames(); + } + public void start(TraceContext ctx, Object req, Object res) { Configure conf = Configure.getInstance(); HttpServletRequest request = (HttpServletRequest) req; @@ -338,48 +445,6 @@ private void processEndUserData(HttpServletRequest request) { } - private String getRequestURI(HttpServletRequest request) { - String uri = request.getRequestURI(); - if (uri == null) - return "no-url"; - int x = uri.indexOf(';'); - if (x > 0) - return uri.substring(0, x); - else - return uri; - } - - private String getRemoteAddr(HttpServletRequest request) { - try { - //For Testing - if (__ip_dummy_test) { - return getRandomIp(); - } - - if (remote_by_header) { - String remoteIp = request.getHeader(http_remote_ip_header_key); - if (remoteIp == null) { - return request.getRemoteAddr(); - } - - int commaPos = remoteIp.indexOf(','); - if (commaPos > -1) { - remoteIp = remoteIp.substring(0, commaPos); - } - - Logger.trace("remoteIp: " + remoteIp); - return remoteIp; - - } else { - return request.getRemoteAddr(); - } - - } catch (Throwable t) { - remote_by_header = false; - return "0.0.0.0"; - } - } - private String getRandomIp() { int len = ipRandom.length; int randomNum = (int) (Math.random() * (len-1)); @@ -495,4 +560,5 @@ public void setSelfDispatch(Object oAsyncContext, boolean self) { public boolean isSelfDispatch(Object oAsyncContext) { return false; } + } diff --git a/scouter.agent.java/src/main/java/scouter/xtra/http/UseridUtil.java b/scouter.agent.java/src/main/java/scouter/xtra/http/UseridUtil.java index 715f13057..fc81394c5 100644 --- a/scouter.agent.java/src/main/java/scouter/xtra/http/UseridUtil.java +++ b/scouter.agent.java/src/main/java/scouter/xtra/http/UseridUtil.java @@ -1,8 +1,8 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -12,9 +12,13 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License. + * limitations under the License. */ package scouter.xtra.http; + +import org.springframework.http.ResponseCookie; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import scouter.agent.Logger; import scouter.util.HashUtil; import scouter.util.Hexa32; @@ -25,6 +29,7 @@ import javax.servlet.http.HttpServletResponse; public class UseridUtil { private static final String SCOUTE_R = "SCOUTER"; + public static long getUserid(HttpServletRequest req, HttpServletResponse res, String cookiePath, int maxAge) { try { String cookie = req.getHeader("Cookie"); @@ -55,6 +60,41 @@ public static long getUserid(HttpServletRequest req, HttpServletResponse res, St } return 0; } + + public static long getUserid(ServerHttpRequest req, ServerHttpResponse res, String cookiePath) { + try { + String cookie = req.getHeaders().getFirst("Cookie"); + if (cookie != null) { + int x1 = cookie.indexOf(SCOUTE_R); + if (x1 >= 0) { + String value = null; + int x2 = cookie.indexOf(';', x1); + if (x2 > 0) { + value = cookie.substring(x1 + SCOUTE_R.length() + 1, x2); + } else { + value = cookie.substring(x1 + SCOUTE_R.length() + 1); + } + try { + return Hexa32.toLong32(value); + } catch (Throwable th) { + } + } + } + + ResponseCookie.ResponseCookieBuilder c = ResponseCookie + .from(SCOUTE_R, Hexa32.toString32(KeyGen.next())); + if ( cookiePath != null && cookiePath.trim().length() > 0 ) { + c.path(cookiePath); + } + c.maxAge(Integer.MAX_VALUE); + res.addCookie(c.build()); + + } catch (Throwable t) { + Logger.println("A153", t.toString()); + } + return 0; + } + public static long getUseridCustom(HttpServletRequest req, String key) { if (key == null || key.length() == 0) return 0; @@ -81,6 +121,32 @@ public static long getUseridCustom(HttpServletRequest req, String key) { return 0; } + public static long getUseridCustom(ServerHttpRequest req, ServerHttpResponse res, String key) { + if (key == null || key.length() == 0) + return 0; + try { + String cookie = req.getHeaders().getFirst("Cookie"); + if (cookie != null) { + int x1 = cookie.indexOf(key); + if (x1 >= 0) { + String value = null; + int x2 = cookie.indexOf(';', x1); + if (x2 > 0) { + value = cookie.substring(x1 + key.length() + 1, x2); + } else { + value = cookie.substring(x1 + key.length() + 1); + } + if (value != null) { + return HashUtil.hash(value); + } + } + } + } catch (Throwable t) { + Logger.println("A154", t.toString()); + } + return 0; + } + public static long getUseridFromHeader(HttpServletRequest req, String key) { if (key == null || key.length() == 0) return 0; @@ -94,4 +160,18 @@ public static long getUseridFromHeader(HttpServletRequest req, String key) { } return 0; } + + public static long getUseridFromHeader(ServerHttpRequest req, ServerHttpResponse res, String key) { + if (key == null || key.length() == 0) + return 0; + try { + String headerValue = req.getHeaders().getFirst(key); + if (headerValue != null) { + return HashUtil.hash(headerValue); + } + } catch (Throwable t) { + Logger.println("A155", t.toString()); + } + return 0; + } } diff --git a/scouter.agent.java/src/main/java/scouter/xtra/http/WebfluxHttpTrace.java b/scouter.agent.java/src/main/java/scouter/xtra/http/WebfluxHttpTrace.java new file mode 100644 index 000000000..bed41e85b --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/http/WebfluxHttpTrace.java @@ -0,0 +1,479 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.xtra.http; + +import org.springframework.http.HttpCookie; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.counter.meter.MeterUsers; +import scouter.agent.netio.data.DataProxy; +import scouter.agent.proxy.IHttpTrace; +import scouter.agent.trace.IProfileCollector; +import scouter.agent.trace.TraceContext; +import scouter.agent.trace.TraceMain; +import scouter.agent.trace.XLogSampler; +import scouter.lang.conf.ConfObserver; +import scouter.lang.constants.B3Constant; +import scouter.lang.step.HashedMessageStep; +import scouter.lang.step.MessageStep; +import scouter.util.CompareUtil; +import scouter.util.HashUtil; +import scouter.util.Hexa32; +import scouter.util.StringUtil; +import scouter.util.zipkin.HexCodec; + +import java.net.URI; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class WebfluxHttpTrace implements IHttpTrace { + boolean remote_by_header; + String http_remote_ip_header_key; + + public WebfluxHttpTrace() { + Configure conf = Configure.getInstance(); + this.http_remote_ip_header_key = conf.trace_http_client_ip_header_key; + this.remote_by_header = !StringUtil.isEmpty(this.http_remote_ip_header_key); + + ConfObserver.add(WebfluxHttpTrace.class.getName(), new Runnable() { + public void run() { + String x = Configure.getInstance().trace_http_client_ip_header_key; + if (CompareUtil.equals(x, http_remote_ip_header_key) == false) { + remote_by_header = StringUtil.isEmpty(x) == false; + http_remote_ip_header_key = x; + } + } + }); + } + + public String getParameter(Object req, String key) { + ServerHttpRequest request = (ServerHttpRequest) req; + if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) + return null; + + return request.getQueryParams().getFirst(key); + } + + private static String getMethod(ServerHttpRequest request) { + HttpMethod method = request.getMethod(); + return method != null ? method.toString() : null; + } + + private static String getQuery(ServerHttpRequest request) { + URI uri = request.getURI(); + return uri != null ? uri.getQuery() : null; + } + + private static String getContentType(ServerHttpRequest request) { + MediaType contentType = request.getHeaders().getContentType(); + return contentType != null ? contentType.toString() : null; + } + + private static String getRequestURI(ServerHttpRequest request) { + URI uri = request.getURI(); + if (uri == null) + return "no-url"; + + String path = uri.getPath(); + if (path == null) + return "no-url"; + + return path; + } + + private static String getHeader(ServerHttpRequest request, String key) { + HttpHeaders headers = request.getHeaders(); + if (headers == null) { + return null; + } + return headers.getFirst(key); + } + + private String getRemoteAddr(ServerHttpRequest request) { + try { + if (remote_by_header) { + String remoteIp = request.getHeaders().getFirst(http_remote_ip_header_key); + if (remoteIp == null) { + return request.getRemoteAddress().getAddress().getHostAddress(); + } + + int commaPos = remoteIp.indexOf(','); + if (commaPos > -1) { + remoteIp = remoteIp.substring(0, commaPos); + } + Logger.trace("remoteIp: " + remoteIp); + return remoteIp; + + } else { + return request.getRemoteAddress().getAddress().getHostAddress(); + } + + } catch (Throwable t) { + remote_by_header = false; + return "0.0.0.0"; + } + } + + public String getHeader(Object req, String key) { + ServerHttpRequest request = (ServerHttpRequest) req; + return getHeader(request, key); + } + + @Override + public String getCookie(Object req, String key) { + ServerHttpRequest request = (ServerHttpRequest) req; + HttpCookie first = request.getCookies().getFirst(key); + return first != null ? first.getValue() : null; + } + + @Override + public String getRequestURI(Object req) { + return getRequestURI((ServerHttpRequest) req); + } + + @Override + public String getRemoteAddr(Object req) { + return getRemoteAddr((ServerHttpRequest) req); + } + + @Override + public String getMethod(Object req) { + return getMethod((ServerHttpRequest) req); + } + + @Override + public String getQueryString(Object req) { + ServerHttpRequest request = (ServerHttpRequest) req; + URI uri = request.getURI(); + return uri != null ? uri.getQuery() : null; + } + + @Override + public Object getAttribute(Object req, String key) { + return null; + } + + @Override + public Enumeration getParameterNames(Object req) { + ServerHttpRequest request = (ServerHttpRequest) req; + Set strings = request.getQueryParams().keySet(); + return Collections.enumeration(strings); + } + + @Override + public Enumeration getHeaderNames(Object req) { + ServerHttpRequest request = (ServerHttpRequest) req; + Set strings = request.getHeaders().keySet(); + return Collections.enumeration(strings); + } + + public void start(TraceContext ctx, Object req, Object res) { + Configure conf = Configure.getInstance(); + ServerHttpRequest request = (ServerHttpRequest) req; + ServerHttpResponse response = (ServerHttpResponse) res; + + ctx.serviceName = getRequestURI(request); + ctx.serviceHash = HashUtil.hash(ctx.serviceName); + + if (ctx.serviceHash == conf.getEndUserPerfEndpointHash()) { + ctx.isStaticContents = true; + //TODO processEndUserData(request); + return; + } + + if (XLogSampler.getInstance().isFullyDiscardServicePattern(ctx.serviceName)) { + ctx.isFullyDiscardService = true; + return; + } + + ctx.isStaticContents = TraceMain.isStaticContents(ctx.serviceName); + + ctx.http_method = getMethod(request); + ctx.http_query = getQuery(request); + ctx.http_content_type = getContentType(request); + ctx.remoteIp = getRemoteAddr(request); + + try { + switch (conf.trace_user_mode) { + case 3: + ctx.userid = UseridUtil.getUseridFromHeader(request, response, conf.trace_user_session_key); + if (ctx.userid == 0 && ctx.remoteIp != null) { + ctx.userid = HashUtil.hash(ctx.remoteIp); + } + break; + case 2: + ctx.userid = UseridUtil.getUserid(request, response, conf.trace_user_cookie_path); + break; + case 1: + ctx.userid = UseridUtil.getUseridCustom(request, response, conf.trace_user_session_key); + if (ctx.userid == 0 && ctx.remoteIp != null) { + ctx.userid = HashUtil.hash(ctx.remoteIp); + } + break; + default: + if (ctx.remoteIp != null) { + ctx.userid = HashUtil.hash(ctx.remoteIp); + } + break; + } + MeterUsers.add(ctx.userid); + } catch (Throwable e) { + // ignore + } + String referer = getHeader(request, "Referer"); + if (referer != null) { + ctx.referer = DataProxy.sendReferer(referer); + } + String userAgent = getHeader(request, "User-Agent"); + if (userAgent != null) { + ctx.userAgent = DataProxy.sendUserAgent(userAgent); + ctx.userAgentString = userAgent; + } + dump(ctx.profile, request, ctx); + if (conf.trace_interservice_enabled) { + try { + boolean b3ModeValid = false; + String b3TraceId = getHeader(request, B3Constant.B3_HEADER_TRACEID); + String gxid = getHeader(request, conf._trace_interservice_gxid_header_key); + + if (gxid != null) { + ctx.gxid = Hexa32.toLong32(gxid); + } else { + if (b3TraceId != null && !b3TraceId.equals("0")) { + b3ModeValid = true; + } + } + + if (b3ModeValid) { + ctx.gxid = HexCodec.lowerHexToUnsignedLong(b3TraceId); + ctx.txid = HexCodec.lowerHexToUnsignedLong(getHeader(request, B3Constant.B3_HEADER_SPANID)); + ctx.caller = HexCodec.lowerHexToUnsignedLong(getHeader(request, B3Constant.B3_HEADER_PARENTSPANID)); + ctx.b3Mode = true; + ctx.b3Traceid = b3TraceId; + + } else { + String txid = getHeader(request, conf._trace_interservice_callee_header_key); + if (txid != null) { + ctx.txid = Hexa32.toLong32(txid); + ctx.is_child_tx = true; + } + String caller = getHeader(request, conf._trace_interservice_caller_header_key); + if (caller != null) { + ctx.caller = Hexa32.toLong32(caller); + ctx.is_child_tx = true; + } + String callerObjHashStr = getHeader(request, conf._trace_interservice_caller_obj_header_key); + if (callerObjHashStr != null) { + try { + ctx.callerObjHash = Integer.parseInt(callerObjHashStr); + } catch (NumberFormatException e) { + } + ctx.is_child_tx = true; + } + } + } catch (Throwable t) { + Logger.println("Z101", "check propergation: " + t.getMessage()); + } + + if (ctx.is_child_tx) { + response.getHeaders().add(conf._trace_interservice_callee_obj_header_key, String.valueOf(conf.getObjHash())); + } + } + + if (conf.trace_response_gxid_enabled && !ctx.isStaticContents) { + try { + if (ctx.gxid == 0) + ctx.gxid = ctx.txid; + + String resGxId = Hexa32.toString32(ctx.gxid) + ":" + ctx.startTime; + response.getHeaders().add(conf._trace_interservice_gxid_header_key, resGxId); + + ResponseCookie c = ResponseCookie.from(conf._trace_interservice_gxid_header_key, resGxId).build(); + response.addCookie(c); + + } catch (Throwable t) { + } + } + + //check queuing from front proxy + if (conf.trace_request_queuing_enabled) { + try { + ctx.queuingHost = getHeader(request, conf.trace_request_queuing_start_host_header); + String startTime = getHeader(request, conf.trace_request_queuing_start_time_header); + if (startTime != null) { + int t = startTime.indexOf("t="); + int ts = startTime.indexOf("ts="); + long startMillis = 0l; + if (t >= 0) { + startMillis = Long.parseLong(startTime.substring(t + 2).trim())/1000; + } else if (ts >= 0) { + startMillis = Long.parseLong(startTime.substring(ts + 3).replace(".", "")); + } + + if (startMillis > 0) { + ctx.queuingTime = (int) (System.currentTimeMillis() - startMillis); + } + } + + ctx.queuing2ndHost = getHeader(request, conf.trace_request_queuing_start_2nd_host_header); + startTime = getHeader(request, conf.trace_request_queuing_start_2nd_time_header); + if (startTime != null) { + int t = startTime.indexOf("t="); + int ts = startTime.indexOf("ts="); + long startMillis = 0l; + if (t >= 0) { + startMillis = Long.parseLong(startTime.substring(t + 2).trim())/1000; + } else if (ts >= 0) { + startMillis = Long.parseLong(startTime.substring(ts + 3).replace(".", "")); + } + + if (startMillis > 0) { + ctx.queuing2ndTime = (int) (System.currentTimeMillis() - startMillis); + } + } + } catch (Throwable t) { + } + } + } + + public void end(TraceContext ctx, Object req, Object res) { + Configure conf = Configure.getInstance(); + + ServerHttpRequest request = (ServerHttpRequest) req; + + if (conf.profile_http_parameter_enabled) { + if (conf.profile_http_parameter_url_prefix == null || ctx.serviceName.indexOf(conf.profile_http_parameter_url_prefix) >= 0) { + String ctype = getContentType(request); + if (ctype != null && ctype.indexOf("multipart") >= 0) + return; + + Enumeration en = getParameterNames(request); + if (en != null) { + int start_time = (int) (System.currentTimeMillis() - ctx.startTime); + while (en.hasMoreElements()) { + String key = (String) en.nextElement(); + String value = new StringBuilder().append("parameter: ").append(key).append("=") + .append(StringUtil.limiting(getParameter(request, key), 1024)).toString(); + + MessageStep step = new MessageStep(value); + step.start_time = start_time; + ctx.profile.add(step); + } + } + } + } + } + + private static void dump(IProfileCollector p, ServerHttpRequest request, TraceContext ctx) { + Configure conf = Configure.getInstance(); + if (conf.profile_http_querystring_enabled) { + String msg = request.getMethod() + " ?" + StringUtil.trimToEmpty(getQuery(request)); + MessageStep step = new MessageStep(msg); + step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); + p.add(step); + } + if (conf.profile_http_header_enabled) { + if (conf.profile_http_header_url_prefix == null || ctx.serviceName.indexOf(conf.profile_http_header_url_prefix) >= 0) { + Set>> entries = request.getHeaders().entrySet(); + if (entries != null) { + int start_time = (int) (System.currentTimeMillis() - ctx.startTime); + Iterator>> iterator = entries.iterator(); + while (iterator.hasNext()) { + for (int i = 0; i < entries.size(); i++) { + Map.Entry> entry = iterator.next(); + if (conf._profile_http_header_keys != null + && conf._profile_http_header_keys.size() > 0 + && !conf._profile_http_header_keys.contains(entry.getKey().toUpperCase())) { + continue; + } + if (entry.getValue() != null) { + for (int j = 0; j < entry.getValue().size(); j++) { + String value = new StringBuilder().append("header: ").append(entry.getKey()).append("=") + .append(StringUtil.limiting(entry.getValue().get(j), 1024)).toString(); + + MessageStep step = new MessageStep(value); + step.start_time = start_time; + + p.add(step); + } + } + } + + } + } + } + } + + if (conf.profile_http_parameter_enabled) { + HashedMessageStep step = new HashedMessageStep(); + step.hash = DataProxy.sendHashedMessage("[HTTP parameters] will be shown in the last of this profile if available.(profile_http_parameter_enabled : true)"); + step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); + step.time = -1; + ctx.profile.add(step); + } + } + + public void rejectText(Object res, String text) { + ServerHttpResponse response = (ServerHttpResponse) res; + try { + //TODO + response.setRawStatusCode(400); + } catch (Exception e) { + } + } + + public void rejectUrl(Object res, String url) { + ServerHttpResponse response = (ServerHttpResponse) res; + try { + //TODO + response.setRawStatusCode(400); + } catch (Exception e) { + } + } + + public void addAsyncContextListener(Object ac) { + return; + } + + public TraceContext getTraceContextFromAsyncContext(Object oAsyncContext) { + return null; + } + + public void setDispatchTransferMap(Object oAsyncContext, long gxid, long caller, long callee, byte xType) { + return; + } + + public void setSelfDispatch(Object oAsyncContext, boolean self) { + return; + } + + public boolean isSelfDispatch(Object oAsyncContext) { + return false; + } + +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/HttpClient43.java b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/HttpClient43.java index cec8c9389..68dbb8bac 100644 --- a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/HttpClient43.java +++ b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/HttpClient43.java @@ -64,6 +64,14 @@ public String getResponseHeader(Object o, String key) { return null; } + public int getResponseStatusCode(Object o) { + if (o instanceof HttpResponse) { + HttpResponse res = (HttpResponse) o; + return res.getStatusLine().getStatusCode(); + } + return 0; + } + public String getURI(Object o) { if (o instanceof HttpUriRequest) { HttpUriRequest req = (HttpUriRequest) o; diff --git a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/JavaNetHttpClient.java b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/JavaNetHttpClient.java index a9f48e34d..a76390e7b 100644 --- a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/JavaNetHttpClient.java +++ b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/JavaNetHttpClient.java @@ -66,4 +66,9 @@ public String getResponseHeader(Object o, String key) { } return null; } + + @Override + public int getResponseStatusCode(Object o) { + return 0; + } } diff --git a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/NettyHttpClient.java b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/NettyHttpClient.java index e3aca31f8..b137d3dd6 100644 --- a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/NettyHttpClient.java +++ b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/NettyHttpClient.java @@ -41,6 +41,14 @@ public String getResponseHeader(Object o, String key) { return null; } + public int getResponseStatusCode(Object o) { + if (o instanceof HttpClientResponse) { + HttpClientResponse res = (HttpClientResponse) o; + return res.getStatus().code(); + } + return 0; + } + public String getURI(Object o) { if (o instanceof HttpClientRequest) { diff --git a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/SpringRestTemplateHttpRequest.java b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/SpringRestTemplateHttpRequest.java index 6258f57b9..ca63c6bd4 100644 --- a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/SpringRestTemplateHttpRequest.java +++ b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/SpringRestTemplateHttpRequest.java @@ -44,6 +44,10 @@ public String getResponseHeader(Object o, String key) { return null; } + public int getResponseStatusCode(Object o) { + return 0; + } + public String getURI(Object o) { if (o instanceof HttpRequest) { HttpRequest chr = (HttpRequest) o; diff --git a/scouter.agent.java/src/main/java/scouter/xtra/httpclient/WebClient.java b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/WebClient.java new file mode 100644 index 000000000..53080e563 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/httpclient/WebClient.java @@ -0,0 +1,62 @@ +package scouter.xtra.httpclient; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.client.reactive.ClientHttpRequest; +import org.springframework.http.client.reactive.ClientHttpResponse; +import scouter.agent.proxy.IHttpClient; + +import java.util.List; + +public class WebClient implements IHttpClient { + public String getHost(Object o) { + if (o instanceof ClientHttpRequest) { + ClientHttpRequest chr = (ClientHttpRequest) o; + return chr.getURI().getHost() + ":" + chr.getURI().getPort(); + } + + return o.toString(); + } + + public void addHeader(Object o, String key, String value) { + if (o instanceof ClientHttpRequest) { + ClientHttpRequest chr = (ClientHttpRequest) o; + HttpHeaders headers = chr.getHeaders(); + headers.set(key, value); + } + } + + public String getHeader(Object o, String key) { + if (o instanceof ClientHttpRequest) { + ClientHttpRequest chr = (ClientHttpRequest) o; + List headerValues = chr.getHeaders().get(key); + if(headerValues != null && headerValues.size() > 0) { + return headerValues.get(0); + } + } + return null; + } + + public String getResponseHeader(Object o, String key) { + if (o instanceof ClientHttpResponse) { + ClientHttpResponse res = (ClientHttpResponse) o; + return res.getHeaders().getFirst(key); + } + return null; + } + + public int getResponseStatusCode(Object o) { + if (o instanceof ClientHttpResponse) { + ClientHttpResponse res = (ClientHttpResponse) o; + return res.getRawStatusCode(); + } + return 0; + } + + public String getURI(Object o) { + if (o instanceof ClientHttpRequest) { + ClientHttpRequest chr = (ClientHttpRequest) o; + return chr.getURI().getPath(); + } + return o.toString(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/java8/ElasticSearchTracer.java b/scouter.agent.java/src/main/java/scouter/xtra/java8/ElasticSearchTracer.java new file mode 100644 index 000000000..83aed6e04 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/java8/ElasticSearchTracer.java @@ -0,0 +1,102 @@ +package scouter.xtra.java8; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.elasticsearch.client.Node; +import scouter.agent.AgentCommonConstant; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.proxy.IElasticSearchTracer; +import scouter.agent.trace.TraceContext; +import scouter.util.LinkedMap; +import scouter.util.StringUtil; + +import java.lang.reflect.Field; + +public class ElasticSearchTracer implements IElasticSearchTracer { + private static final LinkedMap, Field> fieldMap = new LinkedMap, Field>().setMax(30); + boolean err = false; + + private static final Configure conf = Configure.getInstance(); + + @Override + public String getRequestDescription(TraceContext ctx, Object httpRequestBase0) { + return getRequestDescription0(httpRequestBase0, !conf.profile_elasticsearch_full_query_enabled); + } + + @Override + public String getNode(TraceContext ctx, Object hostOrNode) { + if (hostOrNode == null) { + return "Unknown-ElasticSearch"; + } + if (hostOrNode instanceof HttpHost) { + return ((HttpHost) hostOrNode).toHostString(); + + } else if (hostOrNode instanceof Node) { + return ((Node) hostOrNode).getHost().toHostString(); + } else { + return "Unknown-ElasticSearch"; + } + } + + @Override + public Throwable getResponseError(Object httpRequestBase0, Object httpResponse0) { + if (httpResponse0 instanceof HttpResponse) { + HttpResponse resp = (HttpResponse) httpResponse0; + if (resp.getStatusLine() == null) { + return null; + } + if (resp.getStatusLine().getStatusCode() < 400) { + return null; + } + return new RuntimeException(resp.getStatusLine().getStatusCode() + + ": " + resp.toString() + ", [REQUEST]" + getRequestDescription0(httpRequestBase0, false)); + + } else { + return null; + } + } + + private String getRequestDescription0(Object httpRequestBase0, boolean cut) { + if (httpRequestBase0 == null) { + return "No info"; + } + if (httpRequestBase0 instanceof HttpEntityEnclosingRequestBase) { + HttpEntityEnclosingRequestBase requestBase = (HttpEntityEnclosingRequestBase) httpRequestBase0; + String url = requestBase.toString(); + if (cut) { + return StringUtil.limiting(url, 45); + } + HttpEntity entity = requestBase.getEntity(); + try { + Class clazz = entity.getClass(); + Field field = fieldMap.get(clazz); + if (field == null) { + field = clazz.getField(AgentCommonConstant.SCOUTER_ADDED_FIELD); + fieldMap.put(clazz, field); + } + Object entityDesc = field.get(entity); + if (entityDesc == null) { + return url; + } else { + String append = entityDesc instanceof byte[] ? new String((byte[]) entityDesc) + : entityDesc.toString(); + return url + ", entity desc: " + append; + } + } catch (Exception e) { + err = true; + Logger.println("G177p", "error, so skip it later.", e); + return "No info"; + } + + } else { + String url = httpRequestBase0.toString(); + if (!conf.profile_elasticsearch_full_query_enabled) { + return StringUtil.limiting(url, 45); + } + return url; + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer.java b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer.java new file mode 100644 index 000000000..28fce2f5b --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer.java @@ -0,0 +1,158 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.xtra.java8; + +import com.mongodb.MongoNamespace; +import com.mongodb.ReadPreference; +import org.bson.BsonDocument; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.counter.meter.MeterInteraction; +import scouter.agent.counter.meter.MeterInteractionManager; +import scouter.agent.netio.data.DataProxy; +import scouter.agent.trace.StepTransferMap; +import scouter.agent.trace.TraceContext; +import scouter.lang.enumeration.ParameterizedMessageLevel; +import scouter.lang.step.ParameterizedMessageStep; +import scouter.util.StringUtil; + +import java.util.List; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/16 + */ +public class MongoDbTracer { + + public static String COMMAND_QUERY_MSG = "[MongoDB] namespace: %s, query: %s, readPreference: %s"; + public static String COMMAND_COMMAND_MSG = "[MongoDB] namespace: %s, query: %s, payload: %s"; + public static String COMMAND_ERROR_MSG = "[MongoDB] namespace: %s, query: %s\n[Exception:%s] %s"; + public static String COMMAND_COMMAND_ERROR_MSG = "[MongoDB] namespace: %s, query: %s, payload: %s\n[Exception:%s] %s"; + + static Configure conf = Configure.getInstance(); + + public static StepTransferMap.ID generateAndTransferMongoQueryStep(TraceContext ctx, Object _this, String connectionDesc) { + if (ctx == null) { + return null; + } + try { + ParameterizedMessageStep step = new ParameterizedMessageStep(); + step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); + ctx.profile.push(step); + + if (connectionDesc != null) { + step.putTempMessage("connectionDesc", connectionDesc); + } + return StepTransferMap.makeID(ctx, step); + + } catch (Throwable e) { + Logger.println("MDp01", e.getMessage(), e); + return null; + } + } + + public static class ScMongoSingleResultCallback { + public StepTransferMap.ID id; + public Object namespace; + public Object command; + public Object readPreference; + public List payload; + + public ScMongoSingleResultCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, + List payload) { + this.id = id; + this.namespace = namespace; + this.command = command; + this.readPreference = readPreference; + this.payload = payload; + } + + protected void endMongoQueryStep(Throwable throwable) { + try { + if (id == null) { + return; + } + TraceContext ctx = id.ctx; + ParameterizedMessageStep step = (ParameterizedMessageStep) id.step; + if (ctx == null || step == null) { + return; + } + + int elapsed = (int) (System.currentTimeMillis() - ctx.startTime) - step.start_time; + step.setElapsed(elapsed); + + String namespaceDesc = "-"; + String bsonDesc = "-"; + String readPrefDesc = "-"; + String payloadDesc = "-"; + if (namespace instanceof MongoNamespace) { + namespaceDesc = ((MongoNamespace) namespace).getFullName(); + } + if (command instanceof BsonDocument) { + bsonDesc = command.toString(); + } + if (readPreference instanceof ReadPreference) { + readPrefDesc = ((ReadPreference) readPreference).getName(); + } + if (payload != null) { + payloadDesc = payload.toString(); + } + + if (throwable == null) { + step.setLevel(ParameterizedMessageLevel.INFO); + if (readPreference != null) { + step.setMessage(DataProxy.sendHashedMessage(COMMAND_QUERY_MSG), namespaceDesc, bsonDesc, readPrefDesc); + } else { + step.setMessage(DataProxy.sendHashedMessage(COMMAND_COMMAND_MSG), namespaceDesc, bsonDesc, payloadDesc); + } + + } else { + String msg = throwable.getMessage(); + step.setLevel(ParameterizedMessageLevel.ERROR); + + if (readPreference != null) { + step.setMessage(DataProxy.sendHashedMessage(COMMAND_ERROR_MSG), namespaceDesc, bsonDesc, + throwable.getClass().getName(), msg); + } else { + step.setMessage(DataProxy.sendHashedMessage(COMMAND_COMMAND_ERROR_MSG), namespaceDesc, bsonDesc, payloadDesc, + throwable.getClass().getName(), msg); + } + + if (ctx.error == 0 && conf.xlog_error_on_mongodb_exception_enabled) { + ctx.error = DataProxy.sendError(msg); + } + //TODO not yet error summary processing for es : ctx.offerErrorEntity(ErrorEntity.of(throwable, ctx.error, 0, 0)); + } + + ctx.profile.pop(step); + + if (conf.counter_interaction_enabled) { + String connectionDesc = StringUtil.emptyToDefault(step.getTempMessage("connectionDesc"), "-"); + int nodeHash = DataProxy.sendObjName(connectionDesc); + MeterInteraction meterInteraction = MeterInteractionManager.getInstance().getMongoDbCallMeter(conf.getObjHash(), nodeHash); + if (meterInteraction != null) { + meterInteraction.add(elapsed, throwable != null); + } + } + + } catch (Throwable t) { + Logger.println("MDp03", t.getMessage(), t); + } + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer364.java b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer364.java new file mode 100644 index 000000000..60d70414f --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer364.java @@ -0,0 +1,116 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.xtra.java8; + +import com.mongodb.async.SingleResultCallback; +import com.mongodb.connection.InternalConnection; +import com.mongodb.connection.SplittablePayload; +import org.bson.BsonDocument; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.proxy.IMongoDbTracer; +import scouter.agent.trace.StepTransferMap; +import scouter.agent.trace.TraceContext; + +import java.util.List; +import java.util.function.BiConsumer; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/16 + */ +public class MongoDbTracer364 implements IMongoDbTracer { + + static Configure conf = Configure.getInstance(); + + @Override + public StepTransferMap.ID generateAndTransferMongoQueryStep(TraceContext ctx, Object _this, Object connection) { + String connectionDesc = null; + if (connection instanceof InternalConnection) { + connectionDesc = ((InternalConnection) connection).getDescription().getServerAddress().toString(); + } + + return MongoDbTracer.generateAndTransferMongoQueryStep(ctx, _this, connectionDesc); + } + + @Override + public Object genCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, Object payload) { + List payload0 = null; + if (payload instanceof SplittablePayload) { + payload0 = ((SplittablePayload) payload).getPayload(); + } + + ScMongoSingleResultCallback364 callback = + new ScMongoSingleResultCallback364(id, null, namespace, command, readPreference, payload0); + + return new BiConsumer() { + @Override + public void accept(Object o, Throwable throwable) { + callback.endMongoQueryStep(throwable); + } + }; + } + + @Override + public void doCallback(Object callback, Object o, Throwable t) { + if (callback instanceof BiConsumer) { + ((BiConsumer) callback).accept(o, t); + } + } + + @Override + public Object wrapCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, Object payload, Object callback) { + if (id == null) { + return callback; + } + try { + if (callback instanceof SingleResultCallback) { + List payload0 = null; + if (payload instanceof SplittablePayload) { + payload0 = ((SplittablePayload) payload).getPayload(); + } + return new ScMongoSingleResultCallback364(id, (SingleResultCallback) callback, namespace, command, readPreference, payload0); + } else { + return callback; + } + } catch (Throwable e) { + Logger.println("MDp02", e.getMessage(), e); + return callback; + } + } + + public static class ScMongoSingleResultCallback364 extends MongoDbTracer.ScMongoSingleResultCallback + implements SingleResultCallback { + + public SingleResultCallback inner; + + public ScMongoSingleResultCallback364(StepTransferMap.ID id, SingleResultCallback callback, Object namespace, + Object command, Object readPreference, List payload) { + super(id, namespace, command, readPreference, payload); + this.inner = callback; + } + + @Override + public void onResult(T result, Throwable t) { + endMongoQueryStep(t); + if (inner != null) { + inner.onResult(result, t); + } + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer382.java b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer382.java new file mode 100644 index 000000000..1062c470c --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer382.java @@ -0,0 +1,116 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.xtra.java8; + +import com.mongodb.async.SingleResultCallback; +import com.mongodb.connection.SplittablePayload; +import com.mongodb.internal.connection.InternalConnection; +import org.bson.BsonDocument; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.proxy.IMongoDbTracer; +import scouter.agent.trace.StepTransferMap; +import scouter.agent.trace.TraceContext; + +import java.util.List; +import java.util.function.BiConsumer; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/16 + */ +public class MongoDbTracer382 implements IMongoDbTracer { + + static Configure conf = Configure.getInstance(); + + @Override + public StepTransferMap.ID generateAndTransferMongoQueryStep(TraceContext ctx, Object _this, Object connection) { + String connectionDesc = null; + if (connection instanceof InternalConnection) { + connectionDesc = ((InternalConnection) connection).getDescription().getServerAddress().toString(); + } + + return MongoDbTracer.generateAndTransferMongoQueryStep(ctx, _this, connectionDesc); + } + + @Override + public Object genCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, Object payload) { + List payload0 = null; + if (payload instanceof SplittablePayload) { + payload0 = ((SplittablePayload) payload).getPayload(); + } + + ScMongoSingleResultCallback382 callback = + new ScMongoSingleResultCallback382(id, null, namespace, command, readPreference, payload0); + + return new BiConsumer() { + @Override + public void accept(Object o, Throwable throwable) { + callback.endMongoQueryStep(throwable); + } + }; + } + + @Override + public void doCallback(Object callback, Object o, Throwable t) { + if (callback instanceof BiConsumer) { + ((BiConsumer) callback).accept(o, t); + } + } + + @Override + public Object wrapCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, Object payload, Object callback) { + if (id == null) { + return callback; + } + try { + if (callback instanceof SingleResultCallback) { + List payload0 = null; + if (payload instanceof SplittablePayload) { + payload0 = ((SplittablePayload) payload).getPayload(); + } + return new ScMongoSingleResultCallback382(id, (SingleResultCallback) callback, namespace, command, readPreference, payload0); + } else { + return callback; + } + } catch (Throwable e) { + Logger.println("MDp02", e.getMessage(), e); + return callback; + } + } + + public static class ScMongoSingleResultCallback382 extends MongoDbTracer.ScMongoSingleResultCallback + implements SingleResultCallback { + + public SingleResultCallback inner; + + public ScMongoSingleResultCallback382(StepTransferMap.ID id, SingleResultCallback callback, Object namespace, + Object command, Object readPreference, List payload) { + super(id, namespace, command, readPreference, payload); + this.inner = callback; + } + + @Override + public void onResult(T result, Throwable t) { + endMongoQueryStep(t); + if (inner != null) { + inner.onResult(result, t); + } + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer405.java b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer405.java new file mode 100644 index 000000000..bb16166df --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/java8/MongoDbTracer405.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.xtra.java8; + +import com.mongodb.internal.async.SingleResultCallback; +import com.mongodb.internal.connection.InternalConnection; +import com.mongodb.internal.connection.SplittablePayload; +import org.bson.BsonDocument; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.proxy.IMongoDbTracer; +import scouter.agent.trace.StepTransferMap; +import scouter.agent.trace.TraceContext; + +import java.util.List; +import java.util.function.BiConsumer; + +/** + * @author Gun Lee (gunlee01@gmail.com) on 2020/08/16 + */ +public class MongoDbTracer405 implements IMongoDbTracer { + + static Configure conf = Configure.getInstance(); + + @Override + public StepTransferMap.ID generateAndTransferMongoQueryStep(TraceContext ctx, Object _this, Object connection) { + String connectionDesc = null; + if (connection instanceof InternalConnection) { + connectionDesc = ((InternalConnection) connection).getDescription().getServerAddress().toString(); + } + + return MongoDbTracer.generateAndTransferMongoQueryStep(ctx, _this, connectionDesc); + } + + @Override + public Object genCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, Object payload) { + try { + List payload0 = null; + if (payload instanceof SplittablePayload) { + payload0 = ((SplittablePayload) payload).getPayload(); + } + + ScMongoSingleResultCallback405 callback = + new ScMongoSingleResultCallback405(id, null, namespace, command, readPreference, payload0); + + return new BiConsumer() { + @Override + public void accept(Object o, Throwable throwable) { + callback.endMongoQueryStep(throwable); + } + }; + } catch (Throwable t) { + Logger.println("MDp03", t.getMessage(), t); + return null; + } + } + + @Override + public void doCallback(Object callback, Object o, Throwable t) { + try { + if (callback instanceof BiConsumer) { + ((BiConsumer) callback).accept(o, t); + } + } catch (Throwable throwable) { + Logger.println("MDp04", throwable.getMessage(), throwable); + } + } + + @Override + public Object wrapCallback(StepTransferMap.ID id, Object namespace, Object command, Object readPreference, Object payload, Object callback) { + if (id == null) { + return callback; + } + try { + if (callback instanceof SingleResultCallback) { + List payload0 = null; + if (payload instanceof SplittablePayload) { + payload0 = ((SplittablePayload) payload).getPayload(); + } + return new ScMongoSingleResultCallback405(id, (SingleResultCallback) callback, namespace, command, readPreference, payload0); + } else { + return callback; + } + } catch (Throwable e) { + Logger.println("MDp02", e.getMessage(), e); + return callback; + } + } + + public static class ScMongoSingleResultCallback405 extends MongoDbTracer.ScMongoSingleResultCallback + implements SingleResultCallback { + + public SingleResultCallback inner; + + public ScMongoSingleResultCallback405(StepTransferMap.ID id, SingleResultCallback callback, Object namespace, + Object command, Object readPreference, List payload) { + super(id, namespace, command, readPreference, payload); + this.inner = callback; + } + + @Override + public void onResult(T result, Throwable t) { + endMongoQueryStep(t); + if (inner != null) { + inner.onResult(result, t); + } + } + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/reactive/ReactiveSupport.java b/scouter.agent.java/src/main/java/scouter/xtra/reactive/ReactiveSupport.java new file mode 100644 index 000000000..474ae2989 --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/reactive/ReactiveSupport.java @@ -0,0 +1,335 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package scouter.xtra.reactive; + +import kotlin.coroutines.CoroutineContext; +import kotlinx.coroutines.ThreadContextElement; +import kotlinx.coroutines.ThreadContextElementKt; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; +import reactor.core.Fuseable; +import reactor.core.Scannable; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Operators; +import reactor.core.publisher.ScouterOptimizableOperatorProxy; +import reactor.core.publisher.SignalType; +import reactor.util.context.Context; +import scouter.agent.AgentCommonConstant; +import scouter.agent.Configure; +import scouter.agent.Logger; +import scouter.agent.netio.data.DataProxy; +import scouter.agent.proxy.IReactiveSupport; +import scouter.agent.trace.TraceContext; +import scouter.agent.trace.TraceContextManager; +import scouter.agent.trace.TraceMain; +import scouter.lang.enumeration.ParameterizedMessageLevel; +import scouter.lang.step.ParameterizedMessageStep; +import scouter.util.StringUtil; + +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +public class ReactiveSupport implements IReactiveSupport { + + static Configure configure = Configure.getInstance(); + + @Override + public Object subscriptOnContext(Object mono0, final TraceContext traceContext) { + try { + if (traceContext.isReactiveTxidMarked) { + return mono0; + } + Mono mono = (Mono) mono0; + traceContext.isReactiveTxidMarked = true; + return mono.subscriberContext(new Function() { + @Override + public Context apply(Context context) { + return context.put(TraceContext.class, traceContext); + } + }).doOnSuccess(new Consumer() { + @Override + public void accept(Object o) { + TraceMain.endHttpService(new TraceMain.Stat(traceContext), null); + } + }).doOnError(new Consumer() { + @Override + public void accept(Throwable throwable) { + TraceMain.endHttpService(new TraceMain.Stat(traceContext), throwable); + } + }).doOnCancel(new Runnable() { + @Override + public void run() { + TraceMain.endCanceledHttpService(traceContext); + } + }).doFinally(new Consumer() { + @Override + public void accept(SignalType signalType) { + TraceContextManager.clearAllContext(traceContext); + } + }).doAfterTerminate(new Runnable() { + @Override + public void run() { + } + }); + } catch (Throwable e) { + Logger.println("R201", e.getMessage(), e); + return mono0; + } + } + + @Override + public void contextOperatorHook() { + try { + Hooks.onEachOperator(AgentCommonConstant.TRACE_ID, Operators.lift( + new BiFunction, CoreSubscriber>() { + @Override + public CoreSubscriber apply(Scannable scannable, CoreSubscriber subscriber) { + try { + if (scannable instanceof Fuseable.ScalarCallable) { + return subscriber; + } + Context context = subscriber.currentContext(); + TraceContext traceContext = getTraceContext(scannable, context); + + if (traceContext != null) { + return new TxidLifter(subscriber, scannable, null, traceContext); + } else { + return subscriber; + } + } catch (Exception e) { + Logger.println("R1660", e.getMessage(), e); + return subscriber; + } + } + })); + } catch (Throwable e) { + Logger.println("R166", e.getMessage(), e); + } + } + + private TraceContext getTraceContext(Scannable scannable, Context currentContext) { + if (scannable == null || currentContext == null) { + return null; + } + return currentContext.getOrDefault(TraceContext.class, null); + } + + @Override + public Object monoCoroutineContextHook(Object _coroutineContext, TraceContext traceContext) { + try { + CoroutineContext coroutineContext = (CoroutineContext) _coroutineContext; + + TraceContextManager.startByCoroutine(traceContext); + + ThreadContextElement threadContextElement = ThreadContextElementKt + .asContextElement(TraceContextManager.txidByCoroutine, traceContext.txid); + return coroutineContext.plus(threadContextElement); + } catch (Exception e) { + Logger.println("R167p", e.getMessage(), e); + return _coroutineContext; + } + } + + public static class SubscribeDepth {} + public static class TxidLifter implements SpanSubscription, Scannable { + + private final CoreSubscriber coreSubscriber; + private final Context ctx; + private final Scannable scannable; + private final Publisher publisher; + private final TraceContext traceContext; + private final String checkpointDesc; + private final Integer depth; + private Subscription orgSubs; + + private enum ReactorCheckPointType { + ON_SUBSCRIBE, + ON_COMPLETE, + ON_ERROR, + ON_CANCEL + } + + public TxidLifter(CoreSubscriber coreSubscriber, Scannable scannable, Publisher publisher, + TraceContext traceContext) { + this.coreSubscriber = coreSubscriber; + Context context = coreSubscriber.currentContext(); + this.scannable = scannable; + this.publisher = publisher; + this.traceContext = traceContext; + + checkpointDesc = ScouterOptimizableOperatorProxy.nameOnCheckpoint(scannable); + Integer parentDepth = context.getOrDefault(SubscribeDepth.class, 0); + depth = (!"".equals(checkpointDesc)) ? parentDepth + 1 : parentDepth; + this.ctx = context.put(SubscribeDepth.class, depth); + + //todo parent something +// this.ctx = parent != null +// && !parent.equals(ctx.getOrDefault(TraceContext.class, null)) +// ? ctx.put(TraceContext.class, parent) : ctx; + } + + @Override + public void onSubscribe(Subscription subs) { + copyToThread(currentContext(), traceContext); + try { + traceContext.scannables.put(scannable.hashCode(), + new TraceContext.TimedScannable(System.currentTimeMillis(), scannable)); + profileCheckPoint(scannable, traceContext, ReactorCheckPointType.ON_SUBSCRIBE, null); + } catch (Throwable e) { + Logger.println("[R109]", "reactive support onSubscribe error.", e); + } + this.orgSubs = subs; + coreSubscriber.onSubscribe(this); + } + + @Override + public void onNext(T t) { + copyToThread(currentContext(), traceContext); + coreSubscriber.onNext(t); + } + + @Override + public void onError(Throwable throwable) { + copyToThread(currentContext(), traceContext); + try { + TraceContext.TimedScannable timedScannable = traceContext.scannables.remove(scannable.hashCode()); + profileCheckPoint(scannable, traceContext, ReactorCheckPointType.ON_ERROR, timedScannable); + } catch (Throwable e) { + Logger.println("[R110]", "reactive support onError error.", e); + } + coreSubscriber.onError(throwable); + } + + @Override + public void onComplete() { + copyToThread(currentContext(), traceContext); + try { + TraceContext.TimedScannable timedScannable = traceContext.scannables.remove(scannable.hashCode()); + profileCheckPoint(scannable, traceContext, ReactorCheckPointType.ON_COMPLETE, timedScannable); + } catch (Throwable e) { + Logger.println("[R111]", "reactive support onComplete error.", e); + } + coreSubscriber.onComplete(); + } + + @Override + public void request(long n) { + this.orgSubs.request(n); + } + + @Override + public void cancel() { + copyToThread(currentContext(), traceContext); + try { + TraceContext.TimedScannable timedScannable = traceContext.scannables.remove(scannable.hashCode()); + profileCheckPoint(scannable, traceContext, ReactorCheckPointType.ON_CANCEL, timedScannable); + } catch (Throwable e) { + Logger.println("[R112]", "reactive support onCancel error.", e); + } + this.orgSubs.cancel(); + } + + @Override + public Context currentContext() { + return ctx; + } + + @Override + public Object scanUnsafe(Attr key) { + if (key == Attr.PARENT) { + return this.orgSubs; + } + else { + return key == Attr.ACTUAL ? this.coreSubscriber : null; + } + } + + private void copyToThread(Context context, TraceContext traceContext) { + Long threadLocalTxid = TraceContextManager.getLocalTxid(); + if (threadLocalTxid == null) { + TraceContextManager.setTxidLocal(traceContext.txid); + } else if (threadLocalTxid != traceContext.txid) { + TraceContextManager.setTxidLocal(traceContext.txid); + } + } + + private void profileCheckPoint(Scannable scannable, TraceContext traceContext, ReactorCheckPointType type, + TraceContext.TimedScannable timedScannable) { + if (!configure.profile_reactor_checkpoint_enabled) { + return; + } + if (scannable.isScanAvailable()) { + if (!"".equals(checkpointDesc)) { + boolean important = false; + if (checkpointDesc.startsWith("checkpoint")) { + important = true; + } + if (!configure.profile_reactor_more_checkpoint_enabled && !important) { + return; + } + String duration; + StringBuilder messageBuilder = new StringBuilder(300) + .append(StringUtil.padding((depth - 1) * 2, ' ')) + .append("[") + .append(type.name()); + + if (timedScannable != null) { + messageBuilder.append("(%sms): "); + duration = String.valueOf(System.currentTimeMillis() - timedScannable.start); + } else { + messageBuilder.append(": "); + duration = ""; + } + + String message = messageBuilder.append(scannable.name()) + .append("] close checkpoint -> ") + .append(checkpointDesc).toString(); + + ParameterizedMessageStep step = new ParameterizedMessageStep(); + step.setMessage(DataProxy.sendHashedMessage(message), duration); + step.start_time = (int) (System.currentTimeMillis() - traceContext.startTime); + + if (important) { + step.setLevel(ParameterizedMessageLevel.INFO); + } else { + step.setLevel(ParameterizedMessageLevel.DEBUG); + } + traceContext.profile.add(step); + } + } + } + } + + public String dumpScannable(TraceContext traceContext, TraceContext.TimedScannable timedScannable, long now) { + + if (traceContext == null || timedScannable == null) { + return null; + } + Scannable scannable = (Scannable) timedScannable.scannable; + long duration = now - timedScannable.start; + StringBuilder builder = new StringBuilder(1000) + .append(scannable.name()).append(" ").append(duration).append("ms"); + + ScouterOptimizableOperatorProxy.appendSources4Dump(scannable, builder); + return builder.toString(); + } +} diff --git a/scouter.agent.java/src/main/java/scouter/xtra/reactive/SpanSubscription.java b/scouter.agent.java/src/main/java/scouter/xtra/reactive/SpanSubscription.java new file mode 100644 index 000000000..65ed44fdd --- /dev/null +++ b/scouter.agent.java/src/main/java/scouter/xtra/reactive/SpanSubscription.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 the original author or authors. + * @https://github.com/scouter-project/scouter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package scouter.xtra.reactive; + +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; +import reactor.core.Fuseable; + +/** + * A {@link SpanSubscription} is a {@link Subscription} that fakes being {@link Fuseable} + * (implementing {@link Fuseable.QueueSubscription} with default no-op + * methods and always negotiating fusion to be {@link Fuseable#NONE}). + * + * @param - type of the subscription + * @author Marcin Grzejszczak + */ +interface SpanSubscription + extends Subscription, CoreSubscriber, Fuseable.QueueSubscription { + + @Override + default T poll() { + return null; + } + + @Override + default int requestFusion(int i) { + return Fuseable.NONE; // always negotiate to no fusion + } + + @Override + default int size() { + return 0; + } + + @Override + default boolean isEmpty() { + return true; + } + + @Override + default void clear() { + // NO-OP + } + +} diff --git a/scouter.client/src/scouter/client/util/ColorUtil.java b/scouter.client/src/scouter/client/util/ColorUtil.java index 5530be4e5..f0548e702 100644 --- a/scouter.client/src/scouter/client/util/ColorUtil.java +++ b/scouter.client/src/scouter/client/util/ColorUtil.java @@ -84,6 +84,12 @@ private ColorUtil() { rgb.put("gray2", new Color(null, 150, 150, 180)); rgb.put("gray3", new Color(null, 120, 120, 180)); + rgb.put("brown", new Color(null, 165, 42, 42)); + rgb.put("gunlee", new Color(null, 0, 128, 128)); + rgb.put("gunlee2", new Color(null, 0, 102, 204)); + rgb.put("gunlee3", new Color(null, 51, 204, 204)); + rgb.put("gunlee4", new Color(null, 0, 204, 255)); + rgb.put("maroon", new Color(null, 128, 0, 0)); rgb.put("dark orange", new Color(null, 238, 140, 20)); } diff --git a/scouter.client/src/scouter/client/xlog/ProfileText.java b/scouter.client/src/scouter/client/xlog/ProfileText.java index a655cad01..b39e39b43 100644 --- a/scouter.client/src/scouter/client/xlog/ProfileText.java +++ b/scouter.client/src/scouter/client/xlog/ProfileText.java @@ -90,9 +90,9 @@ public static void build(final String date, StyledText text, XLogData xperf, Ste public static Color getColor(ParameterizedMessageLevel level) { switch (level) { case DEBUG: - return ColorUtil.getInstance().getColor("gray2"); - case INFO: return ColorUtil.getInstance().getColor("gray3"); + case INFO: + return ColorUtil.getInstance().getColor("gunlee2"); case WARN: return ColorUtil.getInstance().getColor("dark orange"); case ERROR: diff --git a/scouter.client/src/scouter/client/xlog/XLogFilterStatus.java b/scouter.client/src/scouter/client/xlog/XLogFilterStatus.java index 4e14acb49..e981e4b55 100644 --- a/scouter.client/src/scouter/client/xlog/XLogFilterStatus.java +++ b/scouter.client/src/scouter/client/xlog/XLogFilterStatus.java @@ -23,6 +23,8 @@ public class XLogFilterStatus { public boolean onlySql; public boolean onlyApicall; public boolean onlyError; + public boolean onlySync; + public boolean onlyAsync; public String profileSizeText = ""; public String profileBytesText = ""; @@ -47,6 +49,8 @@ public int hashCode() { filter_hash ^= HashUtil.hash(onlyError ? "onlyError" : ""); filter_hash ^= HashUtil.hash(onlySql ? "onlySql" : ""); filter_hash ^= HashUtil.hash(onlyApicall ? "onlyApicall" : ""); + filter_hash ^= HashUtil.hash(onlySync ? "onlySync" : ""); + filter_hash ^= HashUtil.hash(onlyAsync ? "onlyAsync" : ""); filter_hash ^= HashUtil.hash(profileSizeText); filter_hash ^= HashUtil.hash(profileBytesText); return filter_hash; @@ -73,6 +77,8 @@ public XLogFilterStatus clone() { status.onlySql = onlySql; status.onlyApicall = onlyApicall; status.onlyError = onlyError; + status.onlySync = onlySync; + status.onlyAsync = onlyAsync; return status; } } diff --git a/scouter.client/src/scouter/client/xlog/dialog/XLogFilterDialog.java b/scouter.client/src/scouter/client/xlog/dialog/XLogFilterDialog.java index e145e1904..139b0a61d 100644 --- a/scouter.client/src/scouter/client/xlog/dialog/XLogFilterDialog.java +++ b/scouter.client/src/scouter/client/xlog/dialog/XLogFilterDialog.java @@ -54,6 +54,7 @@ public class XLogFilterDialog extends Dialog { Text serviceTxt, ipTxt, startHmsFromTxt, startHmsToTxt, resTimeFromTxt, resTimeToTxt, userAgentTxt, loginText, descText; Text hasDumpYn, text1Text, text2Text, text3Text, text4Text, text5Text, profileSizeText, profileByteText; Button onlySqlBtn, onlyApiBtn, onlyErrorBtn; + Button onlySyncBtn, onlyAsyncBtn; Button clearBtn, applyBtn; XLogViewCommon view; @@ -386,6 +387,28 @@ public void widgetSelected(SelectionEvent e) { compareHash(); } }); + + onlySyncBtn = new Button(checkGroup, SWT.CHECK); + onlySyncBtn.setText("Sync"); + onlySyncBtn.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, true, false)); + onlySyncBtn.setSelection(status.onlySync); + onlySyncBtn.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + newStatus.onlySync = onlySyncBtn.getSelection(); + compareHash(); + } + }); + + onlyAsyncBtn = new Button(checkGroup, SWT.CHECK); + onlyAsyncBtn.setText("Async"); + onlyAsyncBtn.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, true, false)); + onlyAsyncBtn.setSelection(status.onlyAsync); + onlyAsyncBtn.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + newStatus.onlyAsync = onlyAsyncBtn.getSelection(); + compareHash(); + } + }); Composite btnComp = new Composite(container, SWT.NONE); btnComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, false)); @@ -417,6 +440,8 @@ public void widgetSelected(SelectionEvent e) { onlySqlBtn.setSelection(false); onlyApiBtn.setSelection(false); onlyErrorBtn.setSelection(false); + onlySyncBtn.setSelection(false); + onlyAsyncBtn.setSelection(false); profileSizeText.setText(""); profileByteText.setText(""); newStatus = new XLogFilterStatus(); diff --git a/scouter.client/src/scouter/client/xlog/views/XLogViewPainter.java b/scouter.client/src/scouter/client/xlog/views/XLogViewPainter.java index 68a57bfa8..46d34ed68 100644 --- a/scouter.client/src/scouter/client/xlog/views/XLogViewPainter.java +++ b/scouter.client/src/scouter/client/xlog/views/XLogViewPainter.java @@ -648,6 +648,8 @@ && isUserAgentFilterOk(d) && isErrorFilterOk(d.p) && isApicallFilterOk(d.p) && isSqlFilterOk(d.p) + && isSyncOk(d.p) + && isAsyncOk(d.p) && isProfileSizeFilterOk(d.p) && isProfileByteFilterOk(d.p) ; @@ -866,6 +868,20 @@ public boolean isSqlFilterOk(XLogPack p) { } return true; } + + public boolean isSyncOk(XLogPack p) { + if (filterStatus.onlySync) { + return p.xType != 2 && p.xType != 3 && p.xType != 4; + } + return true; + } + + public boolean isAsyncOk(XLogPack p) { + if (filterStatus.onlyAsync) { + return p.xType == 2 || p.xType == 3 || p.xType == 4; + } + return true; + } public boolean isApicallFilterOk(XLogPack p) { if (filterStatus.onlyApicall) { diff --git a/scouter.common/pom.xml b/scouter.common/pom.xml index 3048e154a..6588e4ea0 100644 --- a/scouter.common/pom.xml +++ b/scouter.common/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-common @@ -30,6 +30,7 @@ maven-compiler-plugin 3.1 + 1.6 1.6 1.6 diff --git a/scouter.common/src/main/java/scouter/lang/counters/CounterConstants.java b/scouter.common/src/main/java/scouter/lang/counters/CounterConstants.java index 71b55aced..e440fcddf 100644 --- a/scouter.common/src/main/java/scouter/lang/counters/CounterConstants.java +++ b/scouter.common/src/main/java/scouter/lang/counters/CounterConstants.java @@ -170,4 +170,6 @@ public class CounterConstants { public static final String INTR_REDIS_CALL = "INTR_REDIS_CALL"; public static final String INTR_KAFKA_CALL = "INTR_KAFKA_CALL"; public static final String INTR_RABBITMQ_CALL = "INTR_RABBITMQ_CALL"; + public static final String INTR_ELASTICSEARCH_CALL = "INTR_ELASTICSEARCH_CALL"; + public static final String INTR_MONGODB_CALL = "INTR_MONGODB_CALL"; } diff --git a/scouter.common/src/main/java/scouter/lang/pack/XLogDiscardTypes.java b/scouter.common/src/main/java/scouter/lang/pack/XLogDiscardTypes.java index 0a2525357..9f216eccb 100644 --- a/scouter.common/src/main/java/scouter/lang/pack/XLogDiscardTypes.java +++ b/scouter.common/src/main/java/scouter/lang/pack/XLogDiscardTypes.java @@ -4,13 +4,15 @@ public class XLogDiscardTypes { public final static byte DISCARD_NONE = 1; public final static byte DISCARD_ALL = 2; public final static byte DISCARD_PROFILE = 3; + public final static byte DISCARD_ALL_FORCE = 4; + public final static byte DISCARD_PROFILE_FORCE = 5; public static byte from(XLogDiscard xLogDiscard) { return xLogDiscard.byteFlag; } public static boolean isAliveXLog(byte n) { - return n == DISCARD_NONE || n == DISCARD_PROFILE; + return n == DISCARD_NONE || n == DISCARD_PROFILE || n == DISCARD_PROFILE_FORCE; } public static boolean isAliveProfile(byte n) { @@ -20,7 +22,9 @@ public static boolean isAliveProfile(byte n) { public enum XLogDiscard { NONE((byte) 1), DISCARD_ALL((byte) 2), - DISCARD_PROFILE((byte) 3) + DISCARD_PROFILE((byte) 3), + DISCARD_ALL_FORCE((byte) 4), + DISCARD_PROFILE_FORCE((byte) 5) ; public byte byteFlag; @@ -28,6 +32,20 @@ public enum XLogDiscard { XLogDiscard(byte byteFlag) { this.byteFlag = byteFlag; } + + public boolean isForceDiscard() { + return this == DISCARD_ALL_FORCE || this == DISCARD_PROFILE_FORCE; + } + + public XLogDiscard toForce() { + if (this == DISCARD_ALL) { + return DISCARD_ALL_FORCE; + } + if (this == DISCARD_PROFILE) { + return DISCARD_PROFILE_FORCE; + } + return this; + } } } diff --git a/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack.java b/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack.java index 1292f6031..2a0a78970 100644 --- a/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack.java +++ b/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack.java @@ -68,6 +68,17 @@ public String toString() { return sb.toString(); } + public String toDebugString(String svcName) { + StringBuilder sb = new StringBuilder(); + sb.append("Profile "); + sb.append(" svcName=").append(svcName); + sb.append(" objHash=").append(Hexa32.toString32(objHash)); + sb.append(" svc=").append(service); + sb.append(" txid=").append(Hexa32.toString32(txid)); + sb.append(" profile=").append(profile == null ? null : profile.length); + return sb.toString(); + } + public void write(DataOutputX dout) throws IOException { dout.writeDecimal(time); dout.writeDecimal(objHash); diff --git a/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack2.java b/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack2.java index fe338229e..95343c818 100644 --- a/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack2.java +++ b/scouter.common/src/main/java/scouter/lang/pack/XLogProfilePack2.java @@ -52,6 +52,23 @@ public String toString() { return sb.toString(); } + public String toDebugString(String svcName) { + StringBuilder sb = new StringBuilder(); + sb.append("Profile2 "); + sb.append(" svcName=").append(svcName); + sb.append(" discardType=").append(discardType); + sb.append(" objHash=").append(Hexa32.toString32(objHash)); + sb.append(" svc=").append(service); + sb.append(" gxid=").append(Hexa32.toString32(gxid)); + sb.append(" txid=").append(Hexa32.toString32(txid)); + sb.append(" packType=").append(getPackType()); + sb.append(" isDriving=").append(isDriving()); + sb.append(" isService=").append(XLogTypes.isService(xType)); + + sb.append(" profile=").append(profile == null ? null : profile.length); + return sb.toString(); + } + public boolean isDriving() { return (gxid == txid) || gxid == 0; } diff --git a/scouter.common/src/main/java/scouter/util/ThreadUtil.java b/scouter.common/src/main/java/scouter/util/ThreadUtil.java index cb546daa5..ae15bd9f7 100644 --- a/scouter.common/src/main/java/scouter/util/ThreadUtil.java +++ b/scouter.common/src/main/java/scouter/util/ThreadUtil.java @@ -72,6 +72,11 @@ public static String getName(Class clazz) { public static MapPack getThreadDetail(long thread_id) { MapPack m = new MapPack(); + return appendThreadDetail(thread_id, m); + } + + public static MapPack appendThreadDetail(long thread_id, MapPack m) { + if (thread_id == 0) return m; ThreadMXBean tmb = ManagementFactory.getThreadMXBean(); diff --git a/scouter.deploy/pom.xml b/scouter.deploy/pom.xml index 3b8ea39b5..dfdac6321 100644 --- a/scouter.deploy/pom.xml +++ b/scouter.deploy/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-deploy diff --git a/scouter.document/main/Configuration.md b/scouter.document/main/Configuration.md index 0c502c9d8..78e32b31d 100644 --- a/scouter.document/main/Configuration.md +++ b/scouter.document/main/Configuration.md @@ -16,7 +16,6 @@ ``` ```java - //Log @ConfigDesc("Logging TCP connection related event") public boolean log_tcp_action_enabled = false; @ConfigDesc("Logging incoming MultiPacket") @@ -49,6 +48,8 @@ public boolean log_udp_batch = false; @ConfigDesc("Logging all request handlers in starting") public boolean log_service_handler_list = false; + @ConfigDesc("Logging incoming SpanPack") + public boolean log_udp_span = false; @ConfigDesc("Logging when index traversal is too heavy.") public int log_index_traversal_warning_count = 100; @@ -105,9 +106,11 @@ public String net_http_api_cors_allow_credentials = "true"; @ConfigDesc("size of webapp connection pool to collector") - public int net_webapp_tcp_client_pool_size = 12; - @ConfigDesc("timeout of web app connection pool to collector(It depends on net_tcp_client_so_timeout_ms)") - public int net_webapp_tcp_client_pool_timeout = net_tcp_client_so_timeout_ms; + public int net_webapp_tcp_client_pool_size = 30; + @ConfigDesc("timeout of web app connection pool to collector") + public int net_webapp_tcp_client_pool_timeout = 60000; + @ConfigDesc("So timeout of web app to collector") + public int net_webapp_tcp_client_so_timeout = 30000; @ConfigDesc("Enable api access control by client ip") public boolean net_http_api_auth_ip_enabled = false; @@ -126,6 +129,8 @@ @ConfigDesc("api access allow ip addresses") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String net_http_api_allow_ips = "localhost,127.0.0.1,0:0:0:0:0:0:0:1,::1"; + public Set allowIpExact; + public List allowIpMatch; //Dir @ConfigDesc("Store directory of database") @@ -145,6 +150,9 @@ @ConfigDesc("inactive object warning level. default 0.(0:info, 1:warn, 2:error, 3:fatal)") public int object_inactive_alert_level = 0; + @ConfigDesc("Zipkin Waiting time(ms) until stopped heartbeat of object is determined to be inactive") + public int object_zipkin_deadtime_ms = 180 * 1000; + //Compress @ConfigDesc("Activating XLog data in zip file") public boolean compress_xlog_enabled = false; @@ -183,22 +191,20 @@ @ConfigDesc("Deprecated : use mgr_purge_xlog_keep_days") public int mgr_purge_xlog_without_profile_keep_days = mgr_purge_xlog_keep_days; - @ConfigDesc("Retaining date for automatic deletion. all counter data.") + @ConfigDesc("Retaining date for automatic deletion") public int mgr_purge_counter_keep_days = 70; @ConfigDesc("Retaining date for automatic deletion. realtime-counter only.") public int mgr_purge_realtime_counter_keep_days = mgr_purge_counter_keep_days; - @ConfigDesc("Retaining date for automatic deletion. tag-counter only.") public int mgr_purge_tag_counter_keep_days = mgr_purge_counter_keep_days; - @ConfigDesc("Retaining date for automatic deletion. visitor-counter only") public int mgr_purge_visitor_counter_keep_days = mgr_purge_counter_keep_days; - @ConfigDesc("Retaining date for automatic deletion. daily text dictionary only") - public int mgr_purge_daily_text_days = Math.max(mgr_purge_tag_counter_keep_days * 2, mgr_purge_xlog_keep_days * 2); + @ConfigDesc("Retaining date for automatic deletion. daily text dictionary only") + public int mgr_purge_daily_text_days = Math.max(mgr_purge_tag_counter_keep_days * 2, mgr_purge_xlog_keep_days * 2); - @ConfigDesc("Retaining date for automatic deletion. summary(stat) data only") + @ConfigDesc("Retaining date for automatic deletion. summary(stat) data only") public int mgr_purge_sum_data_days = 60; @ConfigDesc("Ignored log ID set") @@ -242,6 +248,10 @@ "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") public int _mgr_kv_store_index_default_mb = 8; + @ConfigDesc("change default memory size of xlog txid/gxid index.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_xlog_id_index_mb = 1; + //external-link @ConfigDesc("name of 3rd party ui") public String ext_link_name = "scouter-paper"; @@ -254,6 +264,10 @@ " $[to] : end time in chart by millis") public String ext_link_url_pattern = "http://my-scouter-paper-ip:6188/index.html#/paper?&address=localhost&port=6188&realtime=false&xlogElapsedTime=8000&instances=$[objHashes]&from=$[from]&to=$[to]&layout=my-layout-template-01"; + //Span + @ConfigDesc("Span Queue Size") + public int span_queue_size = 1000; + //XLog @ConfigDesc("XLog Writer Queue Size") public int xlog_queue_size = 10000; @@ -268,6 +282,18 @@ @ConfigDesc("Profile Writer Queue Size") public int profile_queue_size = 1000; + @ConfigDesc("gxid keeping count in memory for XLog consequent sampling") + public int xlog_sampling_matcher_gxid_keep_memory_count = 500000; + @ConfigDesc("xlog keeping count in memory for XLog consequent sampling") + public int xlog_sampling_matcher_xlog_keep_memory_count = 100000; + @ConfigDesc("max keeping millis of xlog for XLog consequent sampling") + public int xlog_sampling_matcher_xlog_keep_memory_millis = 5000; + + @ConfigDesc("profile keeping count (in one bucket, 500ms) in memory for XLog consequent sampling") + public int xlog_sampling_matcher_profile_keep_memory_count = 5000; + @ConfigDesc("max keeping seconds of profile for XLog consequent sampling") + public int xlog_sampling_matcher_profile_keep_memory_secs = 5; + //GeoIP @ConfigDesc("Activating IP-based city/country extraction") public boolean geoip_enabled = true; @@ -286,19 +312,18 @@ @ConfigDesc("search xlog service option - max xlog count to search per request") public int req_search_xlog_max_count = 500; - //telegraf sample config for help - @ConfigDesc("Telegraf http input enabled") + @ConfigDesc("Path to telegraf config xml file") + public String input_telegraf_config_file = CONF_DIR + "scouter-telegraf.xml"; + + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") public boolean input_telegraf_enabled = true; - @ConfigDesc("print telegraf line protocol to STDOUT") + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") public boolean input_telegraf_debug_enabled = false; - - @ConfigDesc("telegraf delta-counter normalize") + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") public boolean input_telegraf_delta_counter_normalize_default = true; - @ConfigDesc("telegraf delta-counter normalize seconds.(avgerage in sec)\n" + - "normalize per metric can be set in the option input_telegraf_$measurement$_counter_mappings") + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") public int input_telegraf_delta_counter_normalize_default_seconds = 30; - - @ConfigDesc("Waiting time(ms) until stopped heartbeat of object is determined to be inactive") + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") public int telegraf_object_deadtime_ms = 35000; @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + @@ -412,7 +437,6 @@ ``` ```java -//Network //Network @ConfigDesc("UDP local IP") public String net_local_udp_ip = null; @@ -451,7 +475,7 @@ @ConfigDesc("Redefining DS, RP type according to main object") public boolean obj_type_inherit_to_child_enabled = false; @ConfigDesc("Activating collect sub counters using JMX") - public boolean jmx_counter_enabled = true; + public boolean jmx_counter_enabled = false; //profile @ConfigDesc("Http Query String profile") @@ -470,7 +494,7 @@ @ConfigDesc("Service URL prefix for Http parameter profile") public String profile_http_parameter_url_prefix = "/"; @ConfigDesc("spring controller method parameter profile") - public boolean profile_spring_controller_method_parameter_enabled = false; + public boolean profile_spring_controller_method_parameter_enabled = true; // @Deprecated // @ConfigDesc("Activating profile summary function") @@ -496,6 +520,8 @@ public boolean profile_method_enabled = true; @ConfigDesc("Profile Buffer Size") public int profile_step_max_count = 1024; + @ConfigDesc("Profile Buffer Size") + public int profile_step_max_keep_in_memory_count = 2048; @ConfigDesc("Stack profile in occurrence of service error") public boolean profile_fullstack_service_error_enabled = false; @ConfigDesc("Stack profile in occurrence of apicall error") @@ -526,6 +552,14 @@ @ConfigDesc("") public boolean profile_fullstack_stmt_leak_enabled = false; + @ConfigDesc("Profile elastic search full query.\nIt need more payload and disk usage.") + public boolean profile_elasticsearch_full_query_enabled = false; + + @ConfigDesc("profile reactor's important checkpoint") + public boolean profile_reactor_checkpoint_enabled = true; + @ConfigDesc("profile reactor's another checkpoints") + public boolean profile_reactor_more_checkpoint_enabled = false; + //Trace @ConfigDesc("User ID based(0 : Remote IP Address, 1 : Cookie(JSESSIONID), 2 : Cookie(SCOUTER), 3 : Header) \n - able to set value for 1.Cookie and 3.Header \n - refer to 'trace_user_session_key'") public int trace_user_mode = 2; // 0:Remote IP, 1:JSessionID, 2:Scouter Cookie, 3:Header @@ -597,6 +631,8 @@ public int _trace_fullstack_socket_open_port = 0; @ConfigDesc("") public int _trace_sql_parameter_max_count = 128; + @ConfigDesc("max length of bound sql parameter on profile view(< 500)") + public int trace_sql_parameter_max_length = 20; @ConfigDesc("") public String trace_delayed_service_mgr_filename = "setting_delayed_service.properties"; @ConfigDesc("") @@ -659,12 +695,16 @@ public int xlog_error_sql_time_max_ms = 30000; @ConfigDesc("Leave an error message at XLog when UserTransaction's begin/end unpaired") public boolean xlog_error_check_user_transaction_enabled = true; - @ConfigDesc("mark as error on xlog flag if SqlException is occurred.") + @ConfigDesc("mark as error on xlog flag if SqlException is occured.") public boolean xlog_error_on_sqlexception_enabled = true; - @ConfigDesc("mark as error on xlog flag if Api call errors are occurred.") + @ConfigDesc("mark as error on xlog flag if Api call errors are occured.") public boolean xlog_error_on_apicall_exception_enabled = true; - @ConfigDesc("mark as error on xlog flag if redis error is occurred.") + @ConfigDesc("mark as error on xlog flag if redis error is occured.") public boolean xlog_error_on_redis_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if elasticsearc error is occured.") + public boolean xlog_error_on_elasticsearch_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if mongodb error is occured.") + public boolean xlog_error_on_mongodb_exception_enabled = true; //XLog hard sampling options @ConfigDesc("XLog hard sampling mode enabled\n - for the best performance but it affects all statistics data") @@ -673,6 +713,14 @@ public int _xlog_hard_sampling_rate_pct = 10; //XLog soft sampling options + @ConfigDesc("XLog sampling - ignore global consequent sampling. the commencement service's sampling option affects it's children.") + public boolean ignore_global_consequent_sampling = false; + @ConfigDesc("XLog sampling - The service of this patterns can be unsampled by the sampling rate even if parent call is sampled and on tracing.") + public String xlog_consequent_sampling_ignore_patterns= ""; + + @ConfigDesc("XLog sampling exclude patterns.") + public String xlog_sampling_exclude_patterns = ""; + @ConfigDesc("XLog sampling mode enabled") public boolean xlog_sampling_enabled = false; @ConfigDesc("XLog sampling but discard profile only not XLog.") @@ -692,10 +740,10 @@ @ConfigDesc("XLog sampling over step3 percentage(%)") public int xlog_sampling_over_rate_pct = 100; + //XLog sampling for service patterns options @ConfigDesc("XLog patterned sampling mode enabled") public boolean xlog_patterned_sampling_enabled = false; - @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String xlog_patterned_sampling_service_patterns = ""; @@ -717,6 +765,102 @@ @ConfigDesc("XLog patterned sampling over step3 percentage(%)") public int xlog_patterned_sampling_over_rate_pct = 100; + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned2_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned2_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned2_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned2_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned2_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned2_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned2_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned2_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned2_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned2_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned3_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned3_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned3_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned3_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned3_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned3_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned3_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned3_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned3_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned3_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned4_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned4_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned4_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned4_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned4_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned4_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned4_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned4_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned4_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned4_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned5_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned5_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned5_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned5_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned5_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned5_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned5_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned5_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned5_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned5_sampling_over_rate_pct = 100; + //XLog discard options @ConfigDesc("XLog discard service patterns\nNo XLog data, but apply to TPS and summary.\neg) /user/{userId},/device/*") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) @@ -782,7 +926,7 @@ public String hook_get_connection_patterns = ""; - @ConfigDesc("InitialContext Class Set") + @ConfigDesc("IntialContext Class Set") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_context_classes = "javax/naming/InitialContext"; @@ -794,7 +938,7 @@ @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_method_ignore_prefixes = "get,set"; - @ConfigDesc("Class set without Method hooking") + @ConfigDesc("Class set without Method hookingt") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_method_ignore_classes = ""; @@ -817,6 +961,9 @@ @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_service_patterns = ""; + @ConfigDesc("hooking service name use a 1st string parameter or class & method name") + public boolean hook_service_name_use_1st_string_enabled = true; + @ConfigDesc("Method set for apicall hooking") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_apicall_patterns = ""; @@ -829,7 +976,7 @@ @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_jsp_patterns = ""; - @ConfigDesc("Method set for preparedstatement hooking") + @ConfigDesc("Method set for preparestatement hooking") @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String hook_jdbc_pstmt_classes = ""; @@ -883,7 +1030,7 @@ @ConfigDesc("Deprecated. use hook_async_callrunnable_enabled") public boolean hook_async_callrunnable_enable = true; - @ConfigDesc("Hook callable and runnable for tracing async processing.\n It hook only 'hook_async_callrunnable_scan_prefixes' option contains package or classes") + @ConfigDesc("Hook callable and runnable for tracing async processing.\n It hook only 'hook_async_callrunnable_scan_prefixes' option contains pacakage or classes") public boolean hook_async_callrunnable_enabled = true; @ConfigDesc("scanning range prefixes for hooking callable, runnable implementations and lambda expressions.\n usually your application package.\n 2 or more packages can be separated by commas.") @@ -894,16 +1041,16 @@ @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) public String _hook_redis_set_key_patterns = ""; - @ConfigDesc("PRE-released option before stable release!\nhook threadpool executor for tracing async processing.") - public boolean hook_async_thread_pool_executor_enabled = false; + @ConfigDesc("hook threadpool executor for tracing async processing.") + public boolean hook_async_thread_pool_executor_enabled = true; - @ConfigDesc("Experimental! test it on staging environment of your system before enable this option.\n enable lambda expressioned class hook for detecting asynchronous processing. \nOnly classes under the package configured by 'hook_async_callrunnable_scan_package_prefixes' is hooked.") - public boolean hook_lambda_instrumentation_strategy_enabled = false; + @ConfigDesc("hystrix execution hook enabled") + public boolean hook_hystrix_enabled = false; @ConfigDesc("") public String hook_add_fields = ""; @ConfigDesc("") - public boolean _hook_service_enabled = true; + public boolean _hook_serivce_enabled = true; @ConfigDesc("") public boolean _hook_dbsql_enabled = true; @ConfigDesc("") @@ -926,6 +1073,22 @@ public boolean _hook_spring_rest_enabled = true; @ConfigDesc("") public boolean _hook_redis_enabled = true; + @ConfigDesc("") + public boolean _hook_kafka_enabled = true; + @ConfigDesc("") + public boolean _hook_elasticsearch_enabled = true; + @ConfigDesc("") + public boolean hook_mongodb_enabled = false; + @ConfigDesc("") + public boolean _hook_rabbit_enabled = true; + @ConfigDesc("") + public boolean _hook_reactive_enabled = true; + @ConfigDesc("") + public boolean _hook_coroutine_enabled = true; + @ConfigDesc("") + public boolean _hook_coroutine_debugger_hook_enabled = false; + @ConfigDesc("") + public boolean _hook_thread_name_enabled = false; @ConfigDesc("") public String _hook_direct_patch_classes = ""; @@ -967,11 +1130,36 @@ @ConfigDesc("SFA thread dump Interval(ms)") public int sfa_dump_interval_ms = 10000; - //PSTS(Periodical Stacktrace Step) + //PSTS(Preiodical Stacktrace Step) @ConfigDesc("Activating periodical stacktrace step (write fixed interval thread dump on a profile)") public boolean _psts_enabled = false; @ConfigDesc("PSTS(periodical stacktrace step) thread dump Interval(ms) - hard min limit 2000") public int _psts_dump_interval_ms = 10000; + public boolean _psts_progressive_reactor_thread_trace_enabled = true; + + //Summary + @ConfigDesc("Activating summary function") + public boolean summary_enabled = true; + @ConfigDesc("") + public boolean _summary_connection_leak_fullstack_enabled = false; + @ConfigDesc("") + public int _summary_service_max_count = 5000; + @ConfigDesc("") + public int _summary_sql_max_count = 5000; + @ConfigDesc("") + public int _summary_api_max_count = 5000; + @ConfigDesc("") + public int _summary_ip_max_count = 5000; + @ConfigDesc("") + public int _summary_useragent_max_count = 5000; + @ConfigDesc("") + public int _summary_error_max_count = 500; + @ConfigDesc("") + public int _summary_enduser_nav_max_count = 5000; + @ConfigDesc("") + public int _summary_enduser_ajax_max_count = 5000; + @ConfigDesc("") + public int _summary_enduser_error_max_count = 5000; ``` ## Host agent options diff --git a/scouter.document/main/Configuration_kr.md b/scouter.document/main/Configuration_kr.md index 1ef124cbf..f72b9a113 100644 --- a/scouter.document/main/Configuration_kr.md +++ b/scouter.document/main/Configuration_kr.md @@ -16,84 +16,409 @@ ``` ```java -//Dir -@ConfigDesc("Store directory of database") -public String db_dir = "./database"; -@ConfigDesc("Path to log directory") -public String log_dir = "./logs"; -@ConfigDesc("Path to plugin directory") -public String plugin_dir = "./plugin"; -@ConfigDesc("Path to client related directory") -public String client_dir = "./client"; - -//Log -@ConfigDesc("Retaining log according to date") -public boolean log_rotation_enabled = true; -@ConfigDesc("Keeping period of log") -public int log_keep_days = 31; - -//Network -@ConfigDesc("UDP Port") -public int net_udp_listen_port = 6100; -@ConfigDesc("TCP Port") -public int net_tcp_listen_port = 6100; -@ConfigDesc("Http Port for scouter-pulse") -public int net_http_port = 6180; - -//Object -@ConfigDesc("Waiting time(ms) until stopped heartbeat of object is determined to be inactive") -public int object_deadtime_ms = 8000; -@ConfigDesc("inactive object warning level. default 0.(0:info, 1:warn, 2:error, 3:fatal)") -public int object_inactive_alert_level = 0; - -//Management -@ConfigDesc("Activating automatic deletion function in the database") -public boolean mgr_purge_enabled = true; - -@ConfigDesc("Condition of disk usage for automatic deletion. if lack, delete profile data first exclude today data.") -public int mgr_purge_disk_usage_pct = 80; - -@ConfigDesc("Retaining date for automatic deletion. delete profile data first.") -public int mgr_purge_profile_keep_days = 10; - -@ConfigDesc("Retaining date for automatic deletion.") -public int mgr_purge_xlog_keep_days = 30; - -@ConfigDesc("Retaining date for automatic deletion. all counter data.") -public int mgr_purge_counter_keep_days = 70; - -@ConfigDesc("Retaining date for automatic deletion. realtime-counter only.") -public int mgr_purge_realtime_counter_keep_days = mgr_purge_counter_keep_days; - -@ConfigDesc("Retaining date for automatic deletion. tag-counter only.") -public int mgr_purge_tag_counter_keep_days = mgr_purge_counter_keep_days; - -@ConfigDesc("Retaining date for automatic deletion. visitor-counter only") -public int mgr_purge_visitor_counter_keep_days = mgr_purge_counter_keep_days; - -@ConfigDesc("Retaining date for automatic deletion. daily text dictionary only") -public int mgr_purge_daily_text_days = Math.max(mgr_purge_tag_counter_keep_days * 2, mgr_purge_xlog_keep_days * 2); - -@ConfigDesc("Retaining date for automatic deletion. summary(stat) data only") -public int mgr_purge_sum_data_days = 60; - -//external-link -@ConfigDesc("name of 3rd party ui") -public String ext_link_name = "scouter-paper"; -@ConfigDesc("outgoing link pattern for a 3rd party UI.(client restart required)\n" + - "Context menu in any chart shows the menu 'Open with 3rd-party UI.'\n" + - "* variable patterns : \n" + - " $[objHashes] : comma separated objHash values\n" + - " $[objType] : object type\n" + - " $[from] : start time in chart by millis\n" + - " $[to] : end time in chart by millis") -public String ext_link_url_pattern = "http://my-scouter-paper-ip:6188/index.html#/paper?&address=localhost&port=6188&realtime=false&xlogElapsedTime=8000&instances=$[objHashes]&from=$[from]&to=$[to]&layout=my-layout-template-01"; - -//GeoIP -@ConfigDesc("Activating IP-based city/country extraction") -public boolean geoip_enabled = true; -@ConfigDesc("Path to GeoIP data file") -public String geoip_data_city_file = CONF_DIR + "GeoLiteCity.dat"; + @ConfigDesc("Logging TCP connection related event") + public boolean log_tcp_action_enabled = false; + @ConfigDesc("Logging incoming MultiPacket") + public boolean log_udp_multipacket = false; + @ConfigDesc("Logging expired MultiPacket") + public boolean log_expired_multipacket = true; + @ConfigDesc("Logging all incoming packs") + public boolean log_udp_packet = false; + @ConfigDesc("Logging incoming CounterPack") + public boolean log_udp_counter = false; + @ConfigDesc("Logging incoming PerfInteractionCounterPack") + public boolean log_udp_interaction_counter = false; + @ConfigDesc("Logging incoming XLogPack") + public boolean log_udp_xlog = false; + @ConfigDesc("Logging incoming ProfilePack") + public boolean log_udp_profile = false; + @ConfigDesc("Logging incoming TextPack") + public boolean log_udp_text = false; + @ConfigDesc("Logging incoming AlertPack") + public boolean log_udp_alert = false; + @ConfigDesc("Logging incoming ObjectPack") + public boolean log_udp_object = false; + @ConfigDesc("Logging incoming StatusPack") + public boolean log_udp_status = false; + @ConfigDesc("Logging incoming StackPack") + public boolean log_udp_stack = false; + @ConfigDesc("Logging incoming SummaryPack") + public boolean log_udp_summary = false; + @ConfigDesc("Logging incoming BatchPack") + public boolean log_udp_batch = false; + @ConfigDesc("Logging all request handlers in starting") + public boolean log_service_handler_list = false; + @ConfigDesc("Logging incoming SpanPack") + public boolean log_udp_span = false; + + @ConfigDesc("Logging when index traversal is too heavy.") + public int log_index_traversal_warning_count = 100; + + @ConfigDesc("Retaining log according to date") + public boolean log_rotation_enabled = true; + @ConfigDesc("Keeping period of log") + public int log_keep_days = 31; + @ConfigDesc("Logging sql failed to parse") + public boolean log_sql_parsing_fail_enabled = false; + @ConfigDesc("") + public boolean _trace = false; + + //Network + @ConfigDesc("UDP Host") + public String net_udp_listen_ip = "0.0.0.0"; + @ConfigDesc("UDP Port") + public int net_udp_listen_port = NetConstants.SERVER_UDP_PORT; + @ConfigDesc("TCP Host") + public String net_tcp_listen_ip = "0.0.0.0"; + @ConfigDesc("TCP Port") + public int net_tcp_listen_port = NetConstants.SERVER_TCP_PORT; + @ConfigDesc("Client Socket Timeout(ms)") + public int net_tcp_client_so_timeout_ms = 8000; + @ConfigDesc("Agent Socket Timeout(ms)") + public int net_tcp_agent_so_timeout_ms = 60000; + @ConfigDesc("Transfer period(ms) of KEEP_ALIVE") + public int net_tcp_agent_keepalive_interval_ms = 5000; + @ConfigDesc("Waiting time(ms) for agent session") + public int net_tcp_get_agent_connection_wait_ms = 1000; + @ConfigDesc("UDP Packet Buffer Size") + public int net_udp_packet_buffer_size = 65535; + @ConfigDesc("UDP Receiver Buffer Size") + public int net_udp_so_rcvbuf_size = 1024 * 1024 * 4; + @ConfigDesc("") + public int _net_udp_worker_thread_count = 3; + @ConfigDesc("TCP Thread Pool Size") + public int net_tcp_service_pool_size = 100; + @ConfigDesc("Activating Http Server") + public boolean net_http_server_enabled = false; + @ConfigDesc("Http Port") + public int net_http_port = NetConstants.SERVER_HTTP_PORT; + @ConfigDesc("user extension web root") + public String net_http_extweb_dir = "./extweb"; + @ConfigDesc("Activating Scouter API") + public boolean net_http_api_enabled = false; + @ConfigDesc("Enable a swagger for HTTP API.") + public boolean net_http_api_swagger_enabled = false; + @ConfigDesc("Swagger option of host's ip or domain to call APIs.") + public String net_http_api_swagger_host_ip = ""; + @ConfigDesc("API CORS support for Access-Control-Allow-Origin") + public String net_http_api_cors_allow_origin = "*"; + @ConfigDesc("Access-Control-Allow-Credentials") + public String net_http_api_cors_allow_credentials = "true"; + + @ConfigDesc("size of webapp connection pool to collector") + public int net_webapp_tcp_client_pool_size = 30; + @ConfigDesc("timeout of web app connection pool to collector") + public int net_webapp_tcp_client_pool_timeout = 60000; + @ConfigDesc("So timeout of web app to collector") + public int net_webapp_tcp_client_so_timeout = 30000; + + @ConfigDesc("Enable api access control by client ip") + public boolean net_http_api_auth_ip_enabled = false; + @ConfigDesc("If get api caller's ip from http header.") + public String net_http_api_auth_ip_header_key; + + @ConfigDesc("Enable api access control by JSESSIONID of Cookie") + public boolean net_http_api_auth_session_enabled = false; + @ConfigDesc("api http session timeout") + public int net_http_api_session_timeout = 3600*24; + @ConfigDesc("Enable api access control by Bearer token(of Authorization http header) - get access token from /user/loginGetToken.") + public boolean net_http_api_auth_bearer_token_enabled = false; + @ConfigDesc("Enable gzip response on api call") + public boolean net_http_api_gzip_enabled = true; + + @ConfigDesc("api access allow ip addresses") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String net_http_api_allow_ips = "localhost,127.0.0.1,0:0:0:0:0:0:0:1,::1"; + public Set allowIpExact; + public List allowIpMatch; + + //Dir + @ConfigDesc("Store directory of database") + public String db_dir = "./database"; + @ConfigDesc("Path to log directory") + public String log_dir = "./logs"; + @ConfigDesc("Path to plugin directory") + public String plugin_dir = "./plugin"; + @ConfigDesc("Path to client related directory") + public String client_dir = "./client"; + @ConfigDesc("temp dir") + public String temp_dir = "./tempdata"; + + //Object + @ConfigDesc("Waiting time(ms) until stopped heartbeat of object is determined to be inactive") + public int object_deadtime_ms = 8000; + @ConfigDesc("inactive object warning level. default 0.(0:info, 1:warn, 2:error, 3:fatal)") + public int object_inactive_alert_level = 0; + + @ConfigDesc("Zipkin Waiting time(ms) until stopped heartbeat of object is determined to be inactive") + public int object_zipkin_deadtime_ms = 180 * 1000; + + //Compress + @ConfigDesc("Activating XLog data in zip file") + public boolean compress_xlog_enabled = false; + @ConfigDesc("Activating profile data in zip file") + public boolean compress_profile_enabled = false; + @ConfigDesc("") + public int _compress_write_buffer_block_count = 3; + @ConfigDesc("") + public int _compress_read_cache_block_count = 3; + @ConfigDesc("") + public long _compress_read_cache_expired_ms = DateUtil.MILLIS_PER_MINUTE; + @ConfigDesc("") + public int _compress_dailycount_header_cache_size = 3; + @ConfigDesc("") + public int _compress_write_thread = 2; + + //Auto + @ConfigDesc("") + public boolean _auto_5m_sampling = true; + + //Manager + @ConfigDesc("Activating automatic deletion function in the database") + public boolean mgr_purge_enabled = true; + @ConfigDesc("Condition of disk usage for automatic deletion. if lack, delete profile data first exclude today data.") + public int mgr_purge_disk_usage_pct = 80; + + @ConfigDesc("Retaining date for automatic deletion. delete profile data first.") + public int mgr_purge_profile_keep_days = 10; + @Deprecated + @ConfigDesc("Deprecated : use mgr_purge_profile_keep_days") + public int mgr_purge_keep_days = mgr_purge_profile_keep_days; + + @ConfigDesc("Retaining date for automatic deletion.") + public int mgr_purge_xlog_keep_days = 30; + @Deprecated + @ConfigDesc("Deprecated : use mgr_purge_xlog_keep_days") + public int mgr_purge_xlog_without_profile_keep_days = mgr_purge_xlog_keep_days; + + @ConfigDesc("Retaining date for automatic deletion") + public int mgr_purge_counter_keep_days = 70; + + @ConfigDesc("Retaining date for automatic deletion. realtime-counter only.") + public int mgr_purge_realtime_counter_keep_days = mgr_purge_counter_keep_days; + @ConfigDesc("Retaining date for automatic deletion. tag-counter only.") + public int mgr_purge_tag_counter_keep_days = mgr_purge_counter_keep_days; + @ConfigDesc("Retaining date for automatic deletion. visitor-counter only") + public int mgr_purge_visitor_counter_keep_days = mgr_purge_counter_keep_days; + + @ConfigDesc("Retaining date for automatic deletion. daily text dictionary only") + public int mgr_purge_daily_text_days = Math.max(mgr_purge_tag_counter_keep_days * 2, mgr_purge_xlog_keep_days * 2); + + @ConfigDesc("Retaining date for automatic deletion. summary(stat) data only") + public int mgr_purge_sum_data_days = 60; + + @ConfigDesc("Ignored log ID set") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public StringSet mgr_log_ignore_ids = new StringSet(); + + //db + @ConfigDesc("true for daily dictionary mode about service name. default value is false that means it's permanent.") + public boolean mgr_text_db_daily_service_enabled = false; + @ConfigDesc("true for daily dictionary mode about api name. default value is false that means it's permanent.") + public boolean mgr_text_db_daily_api_enabled = false; + @ConfigDesc("true for daily dictionary mode about user agent. default value is false that means it's permanent.") + public boolean mgr_text_db_daily_ua_enabled = false; + + @ConfigDesc("change default memory size of hash index.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_default_mb = 1; + @ConfigDesc("change memory size of hash index for service text.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_service_mb = 1; + @ConfigDesc("change memory size of hash index for apicall text.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_api_mb = 1; + @ConfigDesc("change memory size of hash index for user agent text.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_ua_mb = 1; + @ConfigDesc("change memory size of hash index for login text.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_login_mb = 1; + @ConfigDesc("change memory size of hash index for desc text.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_desc_mb = 1; + @ConfigDesc("change memory size of hash index for hashed message text.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_index_hmsg_mb = 1; + @ConfigDesc("change memory size of hash index for daily text db.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_text_db_daily_index_mb = 1; + + @ConfigDesc("change default memory size of key value store index.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_kv_store_index_default_mb = 8; + + @ConfigDesc("change default memory size of xlog txid/gxid index.(MB)" + + "[warn] modified this will break the database files.\nbackup old database files before change values.(restart required)") + public int _mgr_xlog_id_index_mb = 1; + + //external-link + @ConfigDesc("name of 3rd party ui") + public String ext_link_name = "scouter-paper"; + @ConfigDesc("outgoing link pattern for a 3rd party UI.(client restart required)\n" + + "Context menu in any chart shows the menu 'Open with 3rd-party UI.'\n" + + "* variable patterns : \n" + + " $[objHashes] : comma separated objHash values\n" + + " $[objType] : object type\n" + + " $[from] : start time in chart by millis\n" + + " $[to] : end time in chart by millis") + public String ext_link_url_pattern = "http://my-scouter-paper-ip:6188/index.html#/paper?&address=localhost&port=6188&realtime=false&xlogElapsedTime=8000&instances=$[objHashes]&from=$[from]&to=$[to]&layout=my-layout-template-01"; + + //Span + @ConfigDesc("Span Queue Size") + public int span_queue_size = 1000; + + //XLog + @ConfigDesc("XLog Writer Queue Size") + public int xlog_queue_size = 10000; + @ConfigDesc("Ignored time(ms) in retrieving XLog in real time") + public int xlog_realtime_lower_bound_ms = 0; + @ConfigDesc("Ignored time(ms) in retrieving previous XLog") + public int xlog_pasttime_lower_bound_ms = 0; +// @ConfigDesc("Ignored profile time(ms) without saving") +// public int xlog_profile_save_lower_bound_ms = 0; + + //Profile + @ConfigDesc("Profile Writer Queue Size") + public int profile_queue_size = 1000; + + @ConfigDesc("gxid keeping count in memory for XLog consequent sampling") + public int xlog_sampling_matcher_gxid_keep_memory_count = 500000; + @ConfigDesc("xlog keeping count in memory for XLog consequent sampling") + public int xlog_sampling_matcher_xlog_keep_memory_count = 100000; + @ConfigDesc("max keeping millis of xlog for XLog consequent sampling") + public int xlog_sampling_matcher_xlog_keep_memory_millis = 5000; + + @ConfigDesc("profile keeping count (in one bucket, 500ms) in memory for XLog consequent sampling") + public int xlog_sampling_matcher_profile_keep_memory_count = 5000; + @ConfigDesc("max keeping seconds of profile for XLog consequent sampling") + public int xlog_sampling_matcher_profile_keep_memory_secs = 5; + + //GeoIP + @ConfigDesc("Activating IP-based city/country extraction") + public boolean geoip_enabled = true; + @ConfigDesc("Path to GeoIP data file") + public String geoip_data_city_file = CONF_DIR + "GeoLiteCity.dat"; + + //SQL + @ConfigDesc("Activating table-based SQL compression") + public boolean sql_table_parsing_enabled = true; + + //TagCount + @ConfigDesc("Activating TagCount function") + public boolean tagcnt_enabled = true; + + //Service request options from client + @ConfigDesc("search xlog service option - max xlog count to search per request") + public int req_search_xlog_max_count = 500; + + @ConfigDesc("Path to telegraf config xml file") + public String input_telegraf_config_file = CONF_DIR + "scouter-telegraf.xml"; + + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") + public boolean input_telegraf_enabled = true; + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") + public boolean input_telegraf_debug_enabled = false; + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") + public boolean input_telegraf_delta_counter_normalize_default = true; + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") + public int input_telegraf_delta_counter_normalize_default_seconds = 30; + @ConfigDesc("Deprecated use the telegraf config view instead. This value may be ignored.") + public int telegraf_object_deadtime_ms = 35000; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "Telegraf http input of the $measurement$ enabled.\n" + + "$measurement$ is a variable to the measurement name of the line protocol.\n" + + "eg) input_telegraf_$redis_keyspace$_enabled=true") + public boolean input_telegraf_$measurement$_enabled = true; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "print telegraf line protocol of the $measurement$ to STDOUT") + public boolean input_telegraf_$measurement$_debug_enabled = false; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "If set, only the metric matching to this tag value is handled.\n" + + "It can have multiple values. comma separator means 'or' condition. eg) cpu:cpu-total,cpu:cpu0\n" + + "It also have not(!) condition. eg) cpu:!cpu-total") + @ConfigValueType(value = ValueType.COMMA_COLON_SEPARATED_VALUE, + strings = {"filtering tag name(reqiured)", "filtering tag value(reqiured)"}, + booleans = {true, true} + ) + public String input_telegraf_$measurement$_tag_filter = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "which fields of $measurement$ are mapped to scouter's counter.\n" + + "format: {line-protocol field name}:{scouter counter name}:{display name?}:{unit?}:{hasTotal?}:{normalize sec?}\n" + + "It can have multiple values.\n" + + " - {scouter counter name} can be defined in combination with the line protocol's tag variables.\n" + + "For example, if the value of 'tag1' is 'disk01' and the value of 'tag2' is 'bin', the counter name defined as 'scouter-du-$tag1$-$tag2$' is 'scouter-du-disk01-bin'.\n" + + " eg)used_memory:tg-redis-used-memory,used_memory_rss:redis-used-memory-rss,redis used rss,bytes:true\n" + + " eg)cpu:cpu-$cpu-no$ -- this example shows counter definition with tag value.\n" + + "If {line-protocol field name} is started with '&' or '&&', then It works as delta counter\n" + + "When specified as a delta type, the difference in values per second is stored. and the counter name ends with '_delta'\n" + + "double '&&' means BOTH type. AS BOTH type, the value and the difference value both are stored.\n" + + " - {normalize sec} applies only to a delta counter if the counter is a 'BOTH' type counter. (This value can have min 4 to max 60)") + @ConfigValueType(value = ValueType.COMMA_COLON_SEPARATED_VALUE, + strings = {"line protocol field\n(reqiured)", "mapping counter name\n(reqiured)", "display name", "unit", "totalizable\ndefault true", "norm. sec.\ndefault 30"}, + booleans = {true, true, false, false, false, false} + ) + public String input_telegraf_$measurement$_counter_mappings = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "define an obj Family prefix. objectType is defined with some tags.\n" + + "see input_telegraf_$measurement$_objFamily_append_tags option.") + public String input_telegraf_$measurement$_objFamily_base = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "this tags's value is appended to objFamily_base.\nIt can have multiple values. eg)tag1,tag2") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String input_telegraf_$measurement$_objFamily_append_tags = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "define an objectType prefix. objectType is defined with some tags.\n" + + "see input_telegraf_$measurement$_objType_prepend(or append)_tags option.") + public String input_telegraf_$measurement$_objType_base = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "this tags's value is prepended to objType_base.\nIt can have multiple values. eg)tag1,tag2") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String input_telegraf_$measurement$_objType_prepend_tags = "scouter_obj_type_prefix"; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "this tags's value is appended to objType_base.\nIt can have multiple values. eg)tag1,tag2") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String input_telegraf_$measurement$_objType_append_tags = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "this tags's value is object type's icon file name that the scouter client have. eg)redis") + public String input_telegraf_$measurement$_objType_icon = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "define an objectName prefix. objectName is defined with some tags.\n" + + "see input_telegraf_$measurement$_objName_append_tags option.") + public String input_telegraf_$measurement$_objName_base = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "this tags's value is appended to objName_base.\n" + + "It can have multiple values. eg)tag1,tag2") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String input_telegraf_$measurement$_objName_append_tags = ""; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "tag name to define host") + public String input_telegraf_$measurement$_host_tag = "host"; + + @ConfigDesc("[This option is just a sample. Change $measurement$ to your measurement name like $cpu$.]\n" + + "which host value defined with $measurement$_host_tag option is mapped to scouter's host.\n" + + "It can have multiple values. eg)hostValue1:scouterHost1,hostValue2:scouterHost2") + @ConfigValueType(value = ValueType.COMMA_COLON_SEPARATED_VALUE, + strings = {"telegraf host name(reqiured)", "scouter host name(reqiured)"}, + booleans = {true, true} + ) + public String input_telegraf_$measurement$_host_mappings = ""; + + //Visitor Hourly + public boolean visitor_hourly_count_enabled = true; ``` @@ -112,290 +437,729 @@ public String geoip_data_city_file = CONF_DIR + "GeoLiteCity.dat"; ``` ```java -//Network -@ConfigDesc("Collector IP") -public String net_collector_ip = "127.0.0.1"; -@ConfigDesc("Collector UDP Port") -public int net_collector_udp_port = 6100; -@ConfigDesc("Collector TCP Port") -public int net_collector_tcp_port = 6100; + //Network + @ConfigDesc("UDP local IP") + public String net_local_udp_ip = null; + @ConfigDesc("UDP local Port") + public int net_local_udp_port; + @ConfigDesc("Collector IP") + public String net_collector_ip = "127.0.0.1"; + @ConfigDesc("Collector UDP Port") + public int net_collector_udp_port = NetConstants.SERVER_UDP_PORT; + @ConfigDesc("Collector TCP Port") + public int net_collector_tcp_port = NetConstants.SERVER_TCP_PORT; + @ConfigDesc("Collector TCP Session Count") + public int net_collector_tcp_session_count = 1; + @ConfigDesc("Collector TCP Socket Timeout(ms)") + public int net_collector_tcp_so_timeout_ms = 60000; + @ConfigDesc("Collector TCP Connection Timeout(ms)") + public int net_collector_tcp_connection_timeout_ms = 3000; + @ConfigDesc("UDP Buffer Size") + public int net_udp_packet_max_bytes = 60000; + @ConfigDesc("UDP Collection Interval(ms)") + public long net_udp_collection_interval_ms = 100; + + //Object + @ConfigDesc("Deprecated. It's just an alias of monitoring_group_type which overrides this value.") + public String obj_type = ""; + @ConfigDesc("monitoring group type, commonly named as system name and a monitoring type.\neg) ORDER-JVM, WAREHOUSE-LINUX ...") + public String monitoring_group_type = ""; + @ConfigDesc("Object Name") + public String obj_name = ""; + @ConfigDesc("Host Type") + public String obj_host_type = ""; + @ConfigDesc("Host Name") + public String obj_host_name = ""; + @ConfigDesc("Activating for using object name as PID") + public boolean obj_name_auto_pid_enabled = false; + @ConfigDesc("Redefining DS, RP type according to main object") + public boolean obj_type_inherit_to_child_enabled = false; + @ConfigDesc("Activating collect sub counters using JMX") + public boolean jmx_counter_enabled = false; + + //profile + @ConfigDesc("Http Query String profile") + public boolean profile_http_querystring_enabled; + @ConfigDesc("Http Header profile") + public boolean profile_http_header_enabled; + @ConfigDesc("Service URL prefix for Http header profile") + public String profile_http_header_url_prefix = "/"; + + @ConfigDesc("http header names for profiling with comma separator") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String profile_http_header_keys = ""; + + @ConfigDesc("Http Parameter profile") + public boolean profile_http_parameter_enabled; + @ConfigDesc("Service URL prefix for Http parameter profile") + public String profile_http_parameter_url_prefix = "/"; + @ConfigDesc("spring controller method parameter profile") + public boolean profile_spring_controller_method_parameter_enabled = true; + +// @Deprecated +// @ConfigDesc("Activating profile summary function") +// public boolean profile_summary_mode_enabled = false; + + @ConfigDesc("Profiling the memory usage of each method") + public boolean profile_thread_cputime_enabled = false; + @ConfigDesc("Profiling the memory usage of each service") + public boolean profile_thread_memory_usage_enabled = true; + @ConfigDesc("ThreadStack profile for open socket") + public boolean profile_socket_open_fullstack_enabled = false; + @ConfigDesc("ThreadStack profile for a certain port of open socket") + public int profile_socket_open_fullstack_port = 0; + @ConfigDesc("SQL Map profile") + public boolean profile_sqlmap_name_enabled = true; + @ConfigDesc("DBConnection profile") + public boolean profile_connection_open_enabled = true; + @ConfigDesc("Activating stack information profile in opening DB connection") + public boolean profile_connection_open_fullstack_enabled = false; + @ConfigDesc("AutoCommit profile") + public boolean profile_connection_autocommit_status_enabled = false; + @ConfigDesc("Method profile") + public boolean profile_method_enabled = true; + @ConfigDesc("Profile Buffer Size") + public int profile_step_max_count = 1024; + @ConfigDesc("Profile Buffer Size") + public int profile_step_max_keep_in_memory_count = 2048; + @ConfigDesc("Stack profile in occurrence of service error") + public boolean profile_fullstack_service_error_enabled = false; + @ConfigDesc("Stack profile in occurrence of apicall error") + public boolean profile_fullstack_apicall_error_enabled = false; + @ConfigDesc("Stack profile in occurrence of sql error") + public boolean profile_fullstack_sql_error_enabled = false; + @ConfigDesc("Stack profile in occurrence of commit error") + public boolean profile_fullstack_sql_commit_enabled = false; + @ConfigDesc("Stack profile in occurrence of sql error") + public boolean profile_fullstack_hooked_exception_enabled = false; + + @ConfigDesc("Stack profile in occurrence of redis error") + public boolean profile_fullstack_redis_error_enabled = false; + @ConfigDesc("make unknown redis key stringify by force. (using new String(byte[])") + public boolean profile_redis_key_forcibly_stringify_enabled = false; + + @ConfigDesc("Number of stack profile lines in occurrence of error") + public int profile_fullstack_max_lines = 0; + + @ConfigDesc("Escaping literal parameters for normalizing the query") + public boolean profile_sql_escape_enabled = true; + @ConfigDesc("") + public boolean _profile_fullstack_sql_connection_enabled = false; + @ConfigDesc("") + public boolean _profile_fullstack_sql_execute_debug_enabled = false; + @ConfigDesc("") + public boolean profile_fullstack_rs_leak_enabled = false; + @ConfigDesc("") + public boolean profile_fullstack_stmt_leak_enabled = false; + + @ConfigDesc("Profile elastic search full query.\nIt need more payload and disk usage.") + public boolean profile_elasticsearch_full_query_enabled = false; + + @ConfigDesc("profile reactor's important checkpoint") + public boolean profile_reactor_checkpoint_enabled = true; + @ConfigDesc("profile reactor's another checkpoints") + public boolean profile_reactor_more_checkpoint_enabled = false; + + //Trace + @ConfigDesc("User ID based(0 : Remote IP Address, 1 : Cookie(JSESSIONID), 2 : Cookie(SCOUTER), 3 : Header) \n - able to set value for 1.Cookie and 3.Header \n - refer to 'trace_user_session_key'") + public int trace_user_mode = 2; // 0:Remote IP, 1:JSessionID, 2:Scouter Cookie, 3:Header + @ConfigDesc("Setting a cookie expired time for SCOUTER cookie when trace_user_mode is 2") + public int trace_scouter_cookie_max_age = Integer.MAX_VALUE; + @ConfigDesc("Setting a cookie path for SCOUTER cookie when trace_user_mode is 2") + public String trace_user_cookie_path = "/"; + + @ConfigDesc("Tracing background thread socket") + public boolean trace_background_socket_enabled = true; + @ConfigDesc("Adding assigned header value to the service name") + public String trace_service_name_header_key; + @ConfigDesc("Adding assigned get parameter to the service name") + public String trace_service_name_get_key; + @ConfigDesc("Adding assigned post parameter to the service name") + public String trace_service_name_post_key; + @ConfigDesc("Active Thread Warning Time(ms)") + public long trace_activeserivce_yellow_time = 3000; + @ConfigDesc("Active Thread Fatal Time(ms)") + public long trace_activeservice_red_time = 8000; + @ConfigDesc("Identifying header key of Remote IP") + public String trace_http_client_ip_header_key = ""; + @ConfigDesc("Activating gxid connection in HttpTransfer") + public boolean trace_interservice_enabled = true; + @ConfigDesc("") + public String _trace_interservice_gxid_header_key = "X-Scouter-Gxid"; + @ConfigDesc("") + public boolean trace_response_gxid_enabled = false; + @ConfigDesc("") + public String _trace_interservice_callee_header_key = "X-Scouter-Callee"; + @ConfigDesc("") + public String _trace_interservice_caller_header_key = "X-Scouter-Caller"; + @ConfigDesc("") + public String _trace_interservice_caller_obj_header_key = "X-Scouter-Caller-Obj"; + @ConfigDesc("") + public String _trace_interservice_callee_obj_header_key = "X-Scouter-Callee-Obj"; + @ConfigDesc("JSession key for user ID") + public String trace_user_session_key = "JSESSIONID"; + @ConfigDesc("") + public boolean _trace_auto_service_enabled = false; + @ConfigDesc("") + public boolean _trace_auto_service_backstack_enabled = true; + @ConfigDesc("Activating trace DB2") + public boolean trace_db2_enabled = true; + + @Deprecated + @ConfigDesc("Deprecated!") + public boolean trace_webserver_enabled = false; + @Deprecated + @ConfigDesc("Deprecated!") + public String trace_webserver_name_header_key = "X-Forwarded-Host"; + @Deprecated + @ConfigDesc("Deprecated!") + public String trace_webserver_time_header_key = "X-Forwarded-Time"; + + @ConfigDesc("measure queuing time from load balancer, reverse proxy, web server...\n if set, you can open Queuing Time view.") + public boolean trace_request_queuing_enabled = false; + @ConfigDesc("the name of server that set request start time") + public String trace_request_queuing_start_host_header = "X-Request-Start-Host"; + @ConfigDesc("set request start time.\n - time format : t=microsecond (or) ts=second.milli") + public String trace_request_queuing_start_time_header = "X-Request-Start-Time"; + + @ConfigDesc("the name of server that set the trace_request_queuing_start_2nd_time_header") + public String trace_request_queuing_start_2nd_host_header = "X-Request-Start-2nd-Host"; + @ConfigDesc("set request passing time measured by 2nd layered server.\n - time format : t=microsecond (or) ts=second.milli") + public String trace_request_queuing_start_2nd_time_header = "X-Request-Start-2nd-Time"; + + @ConfigDesc("") + public int _trace_fullstack_socket_open_port = 0; + @ConfigDesc("") + public int _trace_sql_parameter_max_count = 128; + @ConfigDesc("max length of bound sql parameter on profile view(< 500)") + public int trace_sql_parameter_max_length = 20; + @ConfigDesc("") + public String trace_delayed_service_mgr_filename = "setting_delayed_service.properties"; + @ConfigDesc("") + public boolean trace_rs_leak_enabled = false; + @ConfigDesc("") + public boolean trace_stmt_leak_enabled = false; + + //Dir + @ConfigDesc("Plugin directory") + public File plugin_dir = new File(agent_dir_path + "/plugin"); + @ConfigDesc("Dump directory") + public File dump_dir = new File(agent_dir_path + "/dump"); + //public File mgr_agent_lib_dir = new File("./_scouter_"); + + //Manager + @ConfigDesc("") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String mgr_static_content_extensions = "js, htm, html, gif, png, jpg, css"; + @ConfigDesc("") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String mgr_log_ignore_ids = ""; + + //Auto dump options when active service is exceed the set threshold count. + @ConfigDesc("Activating auto dump - append dumps onto the dump file in dump directory.") + public boolean autodump_enabled = false; + @ConfigDesc("Auto dump trigger point (dump when exceeding this active service count)") + public int autodump_trigger_active_service_cnt = 10000; + @ConfigDesc("Minimum interval(ms) for operating auto dump function - hard min : 5000") + public long autodump_interval_ms = 30000; + @ConfigDesc("Auto dump level (1 : ThreadDump, 2 : active service, 3 : thread list)") + public int autodump_level = 1; // 1:ThreadDump, 2:ActiveService, 3:ThreadList + + //Auto dump options about the thread on stuck + @ConfigDesc("Dump when a thread are running over this time - 0 is disabled") + public int autodump_stuck_thread_ms = 0; + @ConfigDesc("") + public int autodump_stuck_check_interval_ms = 10000; + + //Auto dump options on exceeded process cpu + @ConfigDesc("Enable the function to generate dump file when this process cpu is over than the set threshold") + public boolean autodump_cpu_exceeded_enabled = false; + @ConfigDesc("Threshold of cpu to generate dump file") + public int autodump_cpu_exceeded_threshold_pct = 90; + @ConfigDesc("Threshold of over-cpu-threshold duration") + public int autodump_cpu_exceeded_duration_ms = 30000; + @ConfigDesc("Dump file generation interval") + public int autodump_cpu_exceeded_dump_interval_ms = 3000; + @ConfigDesc("value of how many dump is generated.") + public int autodump_cpu_exceeded_dump_cnt = 3; + + //XLog + @Deprecated + @ConfigDesc("(deprecated) XLog Ignore Time\n - for backward compatibility. Use xlog_sampling_xxx options instead") + public int xlog_lower_bound_time_ms = 0; + + //XLog error marking + @ConfigDesc("Leave an error message at XLog in case of over fetching. (fetch count)") + public int xlog_error_jdbc_fetch_max = 10000; + @ConfigDesc("Leave an error message at XLog in case of over timing query. (ms)") + public int xlog_error_sql_time_max_ms = 30000; + @ConfigDesc("Leave an error message at XLog when UserTransaction's begin/end unpaired") + public boolean xlog_error_check_user_transaction_enabled = true; + @ConfigDesc("mark as error on xlog flag if SqlException is occured.") + public boolean xlog_error_on_sqlexception_enabled = true; + @ConfigDesc("mark as error on xlog flag if Api call errors are occured.") + public boolean xlog_error_on_apicall_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if redis error is occured.") + public boolean xlog_error_on_redis_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if elasticsearc error is occured.") + public boolean xlog_error_on_elasticsearch_exception_enabled = true; + @ConfigDesc("mark as error on xlog flag if mongodb error is occured.") + public boolean xlog_error_on_mongodb_exception_enabled = true; + + //XLog hard sampling options + @ConfigDesc("XLog hard sampling mode enabled\n - for the best performance but it affects all statistics data") + public boolean _xlog_hard_sampling_enabled = false; + @ConfigDesc("XLog hard sampling rate(%) - discard data over the percentage") + public int _xlog_hard_sampling_rate_pct = 10; + + //XLog soft sampling options + @ConfigDesc("XLog sampling - ignore global consequent sampling. the commencement service's sampling option affects it's children.") + public boolean ignore_global_consequent_sampling = false; + @ConfigDesc("XLog sampling - The service of this patterns can be unsampled by the sampling rate even if parent call is sampled and on tracing.") + public String xlog_consequent_sampling_ignore_patterns= ""; + + @ConfigDesc("XLog sampling exclude patterns.") + public String xlog_sampling_exclude_patterns = ""; + + @ConfigDesc("XLog sampling mode enabled") + public boolean xlog_sampling_enabled = false; + @ConfigDesc("XLog sampling but discard profile only not XLog.") + public boolean xlog_sampling_only_profile = false; + @ConfigDesc("XLog sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_sampling_step1_ms = 100; + @ConfigDesc("XLog sampling step1 percentage(%)") + public int xlog_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_sampling_step2_ms = 1000; + @ConfigDesc("XLog sampling step2 percentage(%)") + public int xlog_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_sampling_step3_ms = 3000; + @ConfigDesc("XLog sampling step3 percentage(%)") + public int xlog_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog sampling over step3 percentage(%)") + public int xlog_sampling_over_rate_pct = 100; + + + //XLog sampling for service patterns options + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned2_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned2_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned2_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned2_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned2_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned2_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned2_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned2_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned2_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned2_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned3_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned3_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned3_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned3_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned3_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned3_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned3_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned3_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned3_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned3_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned4_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned4_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned4_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned4_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned4_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned4_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned4_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned4_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned4_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned4_sampling_over_rate_pct = 100; + + //XLog patterned sampling options for another sampling group + @ConfigDesc("XLog patterned sampling mode enabled") + public boolean xlog_patterned5_sampling_enabled = false; + @ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_patterned5_sampling_service_patterns = ""; + + @ConfigDesc("XLog patterned sampling but discard profile only not XLog.") + public boolean xlog_patterned5_sampling_only_profile = false; + @ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") + public int xlog_patterned5_sampling_step1_ms = 100; + @ConfigDesc("XLog patterned sampling step1 percentage(%)") + public int xlog_patterned5_sampling_step1_rate_pct = 3; + @ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") + public int xlog_patterned5_sampling_step2_ms = 1000; + @ConfigDesc("XLog patterned sampling step2 percentage(%)") + public int xlog_patterned5_sampling_step2_rate_pct = 10; + @ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") + public int xlog_patterned5_sampling_step3_ms = 3000; + @ConfigDesc("XLog patterned sampling step3 percentage(%)") + public int xlog_patterned5_sampling_step3_rate_pct = 30; + @ConfigDesc("XLog patterned sampling over step3 percentage(%)") + public int xlog_patterned5_sampling_over_rate_pct = 100; + + //XLog discard options + @ConfigDesc("XLog discard service patterns\nNo XLog data, but apply to TPS and summary.\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_discard_service_patterns = ""; + @ConfigDesc("Do not discard error even if it's discard pattern.") + public boolean xlog_discard_service_show_error = true; + + @ConfigDesc("XLog fully discard service patterns\nNo XLog data, No apply to TPS and summary.\neg) /user/{userId},/device/*") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String xlog_fully_discard_service_patterns = ""; + + //Alert + @ConfigDesc("Limited length of alert message") + public int alert_message_length = 3000; + @ConfigDesc("Minimum interval(ms) in fetching the same alert") + public long alert_send_interval_ms = 10000; + @ConfigDesc("PermGen usage for send alert") + public int alert_perm_warning_pct = 90; + + //Log + @ConfigDesc("") + public boolean _log_asm_enabled; + @ConfigDesc("") + public boolean _log_udp_xlog_enabled; + @ConfigDesc("") + public boolean _log_udp_object_enabled; + @ConfigDesc("") + public boolean _log_udp_counter_enabled; + @ConfigDesc("") + public boolean _log_datasource_lookup_enabled = true; + @ConfigDesc("") + public boolean _log_background_sql = false; + @ConfigDesc("Log directory") + public String log_dir = ""; + @ConfigDesc("Retaining log according to date") + public boolean log_rotation_enabled = true; + @ConfigDesc("Keeping period of log") + public int log_keep_days = 7; + @ConfigDesc("") + public boolean _trace = false; + @ConfigDesc("") + public boolean _trace_use_logger = false; + + //Hook + @ConfigDesc("Method set for argument hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_args_patterns = ""; + + @ConfigDesc("Method set for return hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_return_patterns = ""; + + @ConfigDesc("Method set for constructor hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_constructor_patterns = ""; + + @ConfigDesc("Method set for dbconnection hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_connection_open_patterns = ""; + + @ConfigDesc("Method set for getconnection hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_get_connection_patterns = ""; -@ConfigDesc("Escaping literal parameters for normalizing the query") -public boolean profile_sql_escape_enabled = true; - -//Naming / grouping -@ConfigDesc("Deprecated. It's just an alias of system_group_id which overrides this value.") -public String obj_type = ""; -@ConfigDesc("monitoring group type, commonly named as system name and a monitoring type.\neg) ORDER-JVM, WAREHOUSE-LINUX ...") -public String monitoring_group_type = ""; -public String obj_name = ""; -@ConfigDesc("Host Type") -public String obj_host_type = ""; -@ConfigDesc("Host Name") -public String obj_host_name = ""; -@ConfigDesc("Activating for using object name as PID") - -//profile -@ConfigDesc("Http Query String profile") -public boolean profile_http_querystring_enabled; -@ConfigDesc("Http Header profile") -public boolean profile_http_header_enabled; -@ConfigDesc("Service URL prefix for Http header profile") -public String profile_http_header_url_prefix = "/"; -@ConfigDesc("spring controller method parameter profile") -public boolean profile_spring_controller_method_parameter_enabled = false; -@ConfigDesc("http header names for profiling with comma separator") -public String profile_http_header_keys = ""; -@ConfigDesc("Calculating CPU time by profile") -public boolean profile_thread_cputime_enabled = false; - -//Trace -@ConfigDesc("Adding assigned header value to the service name") -public String trace_service_name_header_key; -@ConfigDesc("Adding assigned get parameter to the service name") -public String trace_service_name_get_key; -@ConfigDesc("Adding assigned post parameter to the service name") -public String trace_service_name_post_key; - -@ConfigDesc("warning color marking threshold duration(ms) on active service view") -public long trace_activeserivce_yellow_time = 3000; -@ConfigDesc("fatal color marking threshold duration(ms) on active service view") -public long trace_activeservice_red_time = 8000; - -@ConfigDesc("Identifying header key of Remote IP") -public String trace_http_client_ip_header_key = ""; - -@ConfigDesc("Activating gxid connection in HttpTransfer") -public boolean trace_interservice_enabled = false; - -@ConfigDesc("Session key for user counting") -public String trace_user_session_key = "JSESSIONID"; - -@ConfigDesc("") -public String trace_delayed_service_mgr_filename = "setting_delayed_service.properties"; - -//trace request queuing -@ConfigDesc("measure queuing time from load balancer, reverse proxy, web server...\n if set, you can open Queuing Time view.") -public boolean trace_request_queuing_enabled = false; -@ConfigDesc("the name of server that set request start time") -public String trace_request_queuing_start_host_header = "X-Request-Start-Host"; -@ConfigDesc("set request start time.\n - time format : t=microsecond (or) ts=second.milli") -public String trace_request_queuing_start_time_header = "X-Request-Start-Time"; - -@ConfigDesc("the name of server that set the trace_request_queuing_start_2nd_time_header") -public String trace_request_queuing_start_2nd_host_header = "X-Request-Start-2nd-Host"; -@ConfigDesc("set request passing time measured by 2nd layered server.\n - time format : t=microsecond (or) ts=second.milli") -public String trace_request_queuing_start_2nd_time_header = "X-Request-Start-2nd-Time"; - -//Dir -@ConfigDesc("Plugin directory") -public File plugin_dir = new File(agent_dir_path + "/plugin"); -@ConfigDesc("Dump directory") -public File dump_dir = new File(agent_dir_path + "/dump"); -//public File mgr_agent_lib_dir = new File("./_scouter_"); - -//Auto dump options when active service count is exceed the set threshold. -@ConfigDesc("Activating auto dump - append dumps onto the dump file in dump directory.") -public boolean autodump_enabled = false; -@ConfigDesc("Auto dump trigger point (dump when exceeding this active service count)") -public int autodump_trigger_active_service_cnt = 10000; -@ConfigDesc("Minimum interval(ms) for operating auto dump function - hard min : 5000") -public long autodump_interval_ms = 30000; -@ConfigDesc("Auto dump level (1 : ThreadDump, 2 : active service, 3 : thread list)") -public int autodump_level = 1; // 1:ThreadDump, 2:ActiveService, 3:ThreadList - -//generate thread dump if stucked threads exsist -@ConfigDesc("Dump when a thread are running over this time - 0 is disabled") -public int autodump_stuck_thread_ms = 0; -@ConfigDesc("") -public int autodump_stuck_check_interval_ms = 10000; - -//Auto dump options on exceeded process cpu -@ConfigDesc("Enable the function to generate dump file when this process cpu is over than the set threshold") -public boolean autodump_cpu_exceeded_enabled = false; -@ConfigDesc("Threshold of cpu to generate dump file") -public int autodump_cpu_exceeded_threshold_pct = 90; -@ConfigDesc("Threshold of over-cpu-threshold duration") -public int autodump_cpu_exceeded_duration_ms = 30000; -@ConfigDesc("Dump file generation interval") -public int autodump_cpu_exceeded_dump_interval_ms = 3000; -@ConfigDesc("value of how many dump is generated.") -public int autodump_cpu_exceeded_dump_cnt = 3; - -//Log -@ConfigDesc("Log directory") -public String log_dir = ""; -@ConfigDesc("Retaining log according to date") -public boolean log_rotation_enabled = true; -@ConfigDesc("Keeping period of log") -public int log_keep_days = 7; - -//Detect spring Rest url -@ConfigDesc("use @RequestMapping value as service name on a spring REST web appplicaiton.") -public boolean _hook_spring_rest_enabled = false; - -//Hook method -@ConfigDesc("Method set for method hooking") -public String hook_method_patterns = ""; - -@ConfigDesc("Prefix ignrore Method hooking") -public String hook_method_ignore_prefixes = "get,set"; -@ConfigDesc("Class set without Method hookingt") -public String hook_method_ignore_classes = ""; -@ConfigDesc("") -public String hook_method_exclude_patterns = ""; -@ConfigDesc("Activating public Method hooking") -public boolean hook_method_access_public_enabled = true; -@ConfigDesc("Activating private Method hooking") -public boolean hook_method_access_private_enabled = false; -@ConfigDesc("Activating protected Method hooking") -public boolean hook_method_access_protected_enabled = false; -@ConfigDesc("Activating default Method hooking") -public boolean hook_method_access_none_enabled = false; - -//this option should be used only if the apllication is non-servlet. -//In case of servlet web application, detect HttpServlet.service() method as hook-service-patterns automatically. -@ConfigDesc("Method set for service hooking") -public String hook_service_patterns = ""; - -//Hook options for pulgin -@ConfigDesc("Method set for argument hooking") -public String hook_args_patterns = ""; -@ConfigDesc("Method set for return hooking") -public String hook_return_patterns = ""; -@ConfigDesc("Method set for constructor hooking") -public String hook_constructor_patterns = ""; - -//hook for exception handling -public String hook_exception_class_patterns = ""; -@ConfigDesc("Exception class exlude patterns") -public String hook_exception_exlude_class_patterns = ""; -@ConfigDesc("Exception handler patterns - exceptions passed to these methods are treated as error on xlog view. (ex) my.app.myHandler.handleException") -public String hook_exception_handler_method_patterns = ""; - -//XLog -//XLog error marking -@ConfigDesc("Leave an error message at XLog in case of over fetching. (fetch count)") -public int xlog_error_jdbc_fetch_max = 10000; -@ConfigDesc("Leave an error message at XLog in case of over timing query. (ms)") -public int xlog_error_sql_time_max_ms = 30000; -@ConfigDesc("Leave an error message at XLog when UserTransaction's begin/end unpaired") -public boolean xlog_error_check_user_transaction_enabled = true; -@ConfigDesc("mark as error on xlog flag if SqlException is occured.") -public boolean xlog_error_on_sqlexception_enabled = true; -@ConfigDesc("mark as error on xlog flag if Api call errors are occured.") -public boolean xlog_error_on_apicall_exception_enabled = true; - -//XLog hard sampling options -@ConfigDesc("XLog hard sampling mode enabled\n - for the best performance but it affects all statistics data") -public boolean _xlog_hard_sampling_enabled = false; -@ConfigDesc("XLog hard sampling rate(%) - discard data over the percentage") -public int _xlog_hard_sampling_rate_pct = 10; - -//XLog soft sampling options -@ConfigDesc("XLog sampling mode enabled") -public boolean xlog_sampling_enabled = false; -@ConfigDesc("XLog sampling but discard profile only not XLog.") -public boolean xlog_sampling_only_profile = false; -@ConfigDesc("XLog sampling bound millisecond - step1(lowest : range - from 0 to here)") -public int xlog_sampling_step1_ms = 100; -@ConfigDesc("XLog sampling step1 percentage(%)") -public int xlog_sampling_step1_rate_pct = 3; -@ConfigDesc("XLog sampling bound millisecond - step2(range - from step1 to here)") -public int xlog_sampling_step2_ms = 1000; -@ConfigDesc("XLog sampling step2 percentage(%)") -public int xlog_sampling_step2_rate_pct = 10; -@ConfigDesc("XLog sampling bound millisecond - step3(highest : range - from step2 to here)") -public int xlog_sampling_step3_ms = 3000; -@ConfigDesc("XLog sampling step3 percentage(%)") -public int xlog_sampling_step3_rate_pct = 30; -@ConfigDesc("XLog sampling over step3 percentage(%)") -public int xlog_sampling_over_rate_pct = 100; - -//XLog sampling for service patterns options -@ConfigDesc("XLog patterned sampling mode enabled") -public boolean xlog_patterned_sampling_enabled = false; -@ConfigDesc("XLog patterned sampling service patterns\neg) /user/{userId},/device/*") -public String xlog_patterned_sampling_service_patterns = ""; -@ConfigDesc("XLog patterned sampling but discard profile only not XLog.") -public boolean xlog_patterned_sampling_only_profile = false; -@ConfigDesc("XLog patterned sampling bound millisecond - step1(lowest : range - from 0 to here)") -public int xlog_patterned_sampling_step1_ms = 100; -@ConfigDesc("XLog patterned sampling step1 percentage(%)") -public int xlog_patterned_sampling_step1_rate_pct = 3; -@ConfigDesc("XLog patterned sampling bound millisecond - step2(range - from step1 to here)") -public int xlog_patterned_sampling_step2_ms = 1000; -@ConfigDesc("XLog patterned sampling step2 percentage(%)") -public int xlog_patterned_sampling_step2_rate_pct = 10; -@ConfigDesc("XLog patterned sampling bound millisecond - step3(highest : range - from step2 to here)") -public int xlog_patterned_sampling_step3_ms = 3000; -@ConfigDesc("XLog patterned sampling step3 percentage(%)") -public int xlog_patterned_sampling_step3_rate_pct = 30; -@ConfigDesc("XLog patterned sampling over step3 percentage(%)") -public int xlog_patterned_sampling_over_rate_pct = 100; - - -//Async processing support -@ConfigDesc("Hook for supporting async servlet") -public boolean hook_async_servlet_enabled = true; -@ConfigDesc("startAsync impl. method patterns") -public String hook_async_servlet_start_patterns = ""; -@ConfigDesc("asyncContext dispatch impl. method patterns") -public String hook_async_context_dispatch_patterns = ""; - -@ConfigDesc("spring async execution submit patterns") -public String hook_spring_async_submit_patterns = ""; -@ConfigDesc("spring async execution hook enabled") -public boolean hook_spring_async_enabled = true; - -@ConfigDesc("PRE-released option before stable release!\nhook threadpool executor for tracing async processing.") -public boolean hook_async_thread_pool_executor_enabled = false; - -@ConfigDesc("Hook callable and runnable for tracing async processing.\n It hook only 'hook_async_callrunnable_scan_prefixes' option contains pacakage or classes") -public boolean hook_async_callrunnable_enabled = true; -@ConfigDesc("scanning range prefixes for hooking callable, runnable implementations and lambda expressions.\n usually your application package.\n 2 or more packages can be separated by commas.") -public String hook_async_callrunnable_scan_package_prefixes = ""; - -@ConfigDesc("Experimental! test it on staging environment of your system before enable this option.\n enable lambda expressioned class hook for detecting asyncronous processing. \nOnly classes under the package configured by 'hook_async_callrunnable_scan_package_prefixes' is hooked.") -public boolean hook_lambda_instrumentation_strategy_enabled = false; -//Control -@ConfigDesc("Activating Reject service") -public boolean control_reject_service_enabled = false; -@ConfigDesc("Minimum count of rejecting active service") -public int control_reject_service_max_count = 10000; -@ConfigDesc("Activating Reject URL") -public boolean control_reject_redirect_url_enabled = false; -@ConfigDesc("Reject Text") -public String control_reject_text = "too many request!!"; -@ConfigDesc("Reject URL") -public String control_reject_redirect_url = "/error.html"; - -// SFA(Stack Frequency Analyzer) -@ConfigDesc("Activating period threaddump function") -public boolean sfa_dump_enabled = false; -@ConfigDesc("SFA thread dump Interval(ms)") -public int sfa_dump_interval_ms = 10000; - -//Trace -@ConfigDesc("User ID based(0 : Remote IP Address, 1 : Cookie(JSESSIONID), 2 : Cookie(SCOUTER), 3 : Header) \n - able to set value for 1.Cookie and 3.Header \n - refer to 'trace_user_session_key'") -public int trace_user_mode = 2; // 0:Remote IP, 1:JSessionID, 2:Scouter Cookie, 3:Header -@ConfigDesc("Setting a cookie expired time for SCOUTER cookie when trace_user_mode is 2") -public int trace_scouter_cookie_max_age = Integer.MAX_VALUE; -@ConfigDesc("Setting a cookie path for SCOUTER cookie when trace_user_mode is 2") -public String trace_user_cookie_path = "/"; - -@ConfigDesc("Path to file creation directory of process ID file") -public String counter_object_registry_path = "/tmp/scouter"; - -@ConfigDesc("think time (ms) of recent user") -public long counter_recentuser_valid_ms = DateUtil.MILLIS_PER_FIVE_MINUTE; - -@ConfigDesc("PermGen usage for send alert") -public int alert_perm_warning_pct = 90; - -@ConfigDesc("") -public String mgr_static_content_extensions = "js, htm, html, gif, png, jpg, css"; + @ConfigDesc("IntialContext Class Set") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_context_classes = "javax/naming/InitialContext"; + + @ConfigDesc("Method set for method hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_method_patterns = ""; + + @ConfigDesc("Prefix without Method hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_method_ignore_prefixes = "get,set"; + + @ConfigDesc("Class set without Method hookingt") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_method_ignore_classes = ""; + + @ConfigDesc("") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_method_exclude_patterns = ""; + + @ConfigDesc("Activating public Method hooking") + public boolean hook_method_access_public_enabled = true; + @ConfigDesc("Activating private Method hooking") + public boolean hook_method_access_private_enabled = false; + @ConfigDesc("Activating protected Method hooking") + public boolean hook_method_access_protected_enabled = false; + @ConfigDesc("Activating none Method hooking") + public boolean hook_method_access_none_enabled = false; + @ConfigDesc("Activating lambda Method hooking") + public boolean hook_method_lambda_enable = true; + + @ConfigDesc("Method set for service hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_service_patterns = ""; + + @ConfigDesc("hooking service name use a 1st string parameter or class & method name") + public boolean hook_service_name_use_1st_string_enabled = true; + + @ConfigDesc("Method set for apicall hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_apicall_patterns = ""; + + @ConfigDesc("Method set for apicallinfo hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_apicall_info_patterns = ""; + + @ConfigDesc("Method set for jsp hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_jsp_patterns = ""; + + @ConfigDesc("Method set for preparestatement hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_jdbc_pstmt_classes = ""; + + @ConfigDesc("Method set for statement hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_jdbc_stmt_classes = ""; + + @ConfigDesc("Method set for resultset hooking") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_jdbc_rs_classes = ""; + + @ConfigDesc("Method set for dbconnection wrapping") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_jdbc_wrapping_driver_patterns = ""; + + @ConfigDesc("Exception class patterns - These will seem as error on xlog view.\n (ex) my.app.BizException,my.app.exception.*Exception") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_exception_class_patterns = ""; + + @ConfigDesc("Exception class exclude patterns") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_exception_exclude_class_patterns = ""; + + @ConfigDesc("Exception handler patterns\n - exceptions passed to these methods are treated as error on xlog view.\n (ex) my.app.myHandler.handleException") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_exception_handler_method_patterns = ""; + + @ConfigDesc("Exception handler exclude class name patterns(can not include star-* in patterns)\n - (ex) my.app.MyManagedException,MyBizException") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_exception_handler_exclude_class_patterns = ""; + + @ConfigDesc("Hook for supporting async servlet") + public boolean hook_async_servlet_enabled = true; + + @ConfigDesc("startAsync impl. method patterns") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_async_servlet_start_patterns = ""; + + @ConfigDesc("asyncContext dispatch impl. method patterns") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_async_context_dispatch_patterns = ""; + + @ConfigDesc("spring async execution submit patterns") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_spring_async_submit_patterns = ""; + + @ConfigDesc("spring async execution hook enabled") + public boolean hook_spring_async_enabled = true; + + @Deprecated + @ConfigDesc("Deprecated. use hook_async_callrunnable_enabled") + public boolean hook_async_callrunnable_enable = true; + + @ConfigDesc("Hook callable and runnable for tracing async processing.\n It hook only 'hook_async_callrunnable_scan_prefixes' option contains pacakage or classes") + public boolean hook_async_callrunnable_enabled = true; + + @ConfigDesc("scanning range prefixes for hooking callable, runnable implementations and lambda expressions.\n usually your application package.\n 2 or more packages can be separated by commas.") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String hook_async_callrunnable_scan_package_prefixes = ""; + + @ConfigDesc("redis key setting patterns.\n refer to org.springframework.data.redis.core.AbstractOperations#rawKey") + @ConfigValueType(ValueType.COMMA_SEPARATED_VALUE) + public String _hook_redis_set_key_patterns = ""; + + @ConfigDesc("hook threadpool executor for tracing async processing.") + public boolean hook_async_thread_pool_executor_enabled = true; + + @ConfigDesc("hystrix execution hook enabled") + public boolean hook_hystrix_enabled = false; + + @ConfigDesc("") + public String hook_add_fields = ""; + @ConfigDesc("") + public boolean _hook_serivce_enabled = true; + @ConfigDesc("") + public boolean _hook_dbsql_enabled = true; + @ConfigDesc("") + public boolean _hook_dbconn_enabled = true; + @ConfigDesc("") + public boolean _hook_cap_enabled = true; + @ConfigDesc("") + public boolean _hook_methods_enabled = true; + @ConfigDesc("") + public boolean _hook_apicall_enabled = true; + @ConfigDesc("") + public boolean _hook_socket_enabled = true; + @ConfigDesc("") + public boolean _hook_jsp_enabled = true; + @ConfigDesc("") + public boolean _hook_async_enabled = true; + @ConfigDesc("") + public boolean _hook_usertx_enabled = true; + @ConfigDesc("") + public boolean _hook_spring_rest_enabled = true; + @ConfigDesc("") + public boolean _hook_redis_enabled = true; + @ConfigDesc("") + public boolean _hook_kafka_enabled = true; + @ConfigDesc("") + public boolean _hook_elasticsearch_enabled = true; + @ConfigDesc("") + public boolean hook_mongodb_enabled = false; + @ConfigDesc("") + public boolean _hook_rabbit_enabled = true; + @ConfigDesc("") + public boolean _hook_reactive_enabled = true; + @ConfigDesc("") + public boolean _hook_coroutine_enabled = true; + @ConfigDesc("") + public boolean _hook_coroutine_debugger_hook_enabled = false; + @ConfigDesc("") + public boolean _hook_thread_name_enabled = false; + + @ConfigDesc("") + public String _hook_direct_patch_classes = ""; + + @ConfigDesc("") + public String _hook_boot_prefix = null; + @ConfigDesc("for warning a big Map type object that have a lot of entities.\n It may increase system load. be careful to enable this option.") + public boolean _hook_map_impl_enabled = false; + @ConfigDesc("") + public int _hook_map_impl_warning_size = 50000; + + //Control + @ConfigDesc("Activating Reject service") + public boolean control_reject_service_enabled = false; + @ConfigDesc("Minimum count of rejecting active service") + public int control_reject_service_max_count = 10000; + @ConfigDesc("Activating Reject URL") + public boolean control_reject_redirect_url_enabled = false; + @ConfigDesc("Reject Text") + public String control_reject_text = "too many request!!"; + @ConfigDesc("Reject URL") + public String control_reject_redirect_url = "/error.html"; + + // Counter + @ConfigDesc("Activating collect counter") + public boolean counter_enabled = true; + @ConfigDesc("think time (ms) of recent user") + public long counter_recentuser_valid_ms = DateUtil.MILLIS_PER_FIVE_MINUTE; + @ConfigDesc("Path to file creation directory of process ID file") + public String counter_object_registry_path = "/tmp/scouter"; + @ConfigDesc("Activating custom jmx") + public boolean counter_custom_jmx_enabled = false; + @ConfigDesc("Activating interaction counter") + public boolean counter_interaction_enabled = false; + + // SFA(Stack Frequency Analyzer) + @ConfigDesc("Activating period threaddump function") + public boolean sfa_dump_enabled = false; + @ConfigDesc("SFA thread dump Interval(ms)") + public int sfa_dump_interval_ms = 10000; + + //PSTS(Preiodical Stacktrace Step) + @ConfigDesc("Activating periodical stacktrace step (write fixed interval thread dump on a profile)") + public boolean _psts_enabled = false; + @ConfigDesc("PSTS(periodical stacktrace step) thread dump Interval(ms) - hard min limit 2000") + public int _psts_dump_interval_ms = 10000; + public boolean _psts_progressive_reactor_thread_trace_enabled = true; + + //Summary + @ConfigDesc("Activating summary function") + public boolean summary_enabled = true; + @ConfigDesc("") + public boolean _summary_connection_leak_fullstack_enabled = false; + @ConfigDesc("") + public int _summary_service_max_count = 5000; + @ConfigDesc("") + public int _summary_sql_max_count = 5000; + @ConfigDesc("") + public int _summary_api_max_count = 5000; + @ConfigDesc("") + public int _summary_ip_max_count = 5000; + @ConfigDesc("") + public int _summary_useragent_max_count = 5000; + @ConfigDesc("") + public int _summary_error_max_count = 500; + @ConfigDesc("") + public int _summary_enduser_nav_max_count = 5000; + @ConfigDesc("") + public int _summary_enduser_ajax_max_count = 5000; + @ConfigDesc("") + public int _summary_enduser_error_max_count = 5000; ``` ## Host agent options diff --git a/scouter.server.boot/pom.xml b/scouter.server.boot/pom.xml index 13b5a9892..13e08bac4 100644 --- a/scouter.server.boot/pom.xml +++ b/scouter.server.boot/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-server-boot diff --git a/scouter.server/pom.xml b/scouter.server/pom.xml index 59369e807..1867ec9d9 100644 --- a/scouter.server/pom.xml +++ b/scouter.server/pom.xml @@ -4,7 +4,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 scouter-server diff --git a/scouter.server/src/main/java/scouter/server/Configure.java b/scouter.server/src/main/java/scouter/server/Configure.java index 123cdb297..b09ce6f05 100644 --- a/scouter.server/src/main/java/scouter/server/Configure.java +++ b/scouter.server/src/main/java/scouter/server/Configure.java @@ -139,6 +139,8 @@ public final static synchronized Configure newInstanceForTestCase() { public String server_id = SysJMX.getHostName(); //Log + public int log_test_rate = 0; + @ConfigDesc("Logging TCP connection related event") public boolean log_tcp_action_enabled = false; @ConfigDesc("Logging incoming MultiPacket") @@ -659,6 +661,8 @@ private boolean reloadConfig() { public static boolean WORKABLE = true; private void applyConfig() { + this.log_test_rate = getInt("log_test_rate", 0); + this.xlog_queue_size = getInt("xlog_queue_size", 10000); this.profile_queue_size = getInt("profile_queue_size", 1000); this.log_tcp_action_enabled = getBoolean("log_tcp_action_enabled", false); diff --git a/scouter.server/src/main/java/scouter/server/core/AutoDeleteScheduler.java b/scouter.server/src/main/java/scouter/server/core/AutoDeleteScheduler.java index aafcbf6f8..b15684bbe 100644 --- a/scouter.server/src/main/java/scouter/server/core/AutoDeleteScheduler.java +++ b/scouter.server/src/main/java/scouter/server/core/AutoDeleteScheduler.java @@ -75,11 +75,16 @@ public void run() { || conf.mgr_purge_daily_text_days != retainTextDays || conf.mgr_purge_sum_data_days != retainSumDays ) { - applyConf(); deletedProfileDays.clear(); deletedXLogDays.clear(); deletedDays.clear(); + + deletedRealtimeCounterDays.clear(); + deletedTagCounterDays.clear(); + deletedVisitorCounterDays.clear(); + deletedTextDays.clear(); + deletedSumDays.clear(); } } }); @@ -109,6 +114,7 @@ public void run() { purgeXlog(today); purgeTagCounter(today); purgeText(today); + purgeSum(today); purgeVisitorCounter(today); purgeRealtimeCounter(today); purgeAllCounter(today); diff --git a/scouter.server/src/main/java/scouter/server/core/cache/XLogDelayingCache.java b/scouter.server/src/main/java/scouter/server/core/cache/XLogDelayingCache.java index 4cececfd6..367291e2d 100644 --- a/scouter.server/src/main/java/scouter/server/core/cache/XLogDelayingCache.java +++ b/scouter.server/src/main/java/scouter/server/core/cache/XLogDelayingCache.java @@ -75,7 +75,7 @@ public boolean isProcessedGxidWithoutProfile(long gxid) { public boolean isProcessedGxidWithProfile(long gxid) { byte discardType = (byte) processedGxidMap.get(gxid); - if (discardType == 0 || XLogDiscardTypes.isAliveProfile(discardType)) { + if (discardType != 0 && XLogDiscardTypes.isAliveProfile(discardType)) { return true; } return false; diff --git a/scouter.server/src/main/scala/scouter/server/core/ProfilePreCore.scala b/scouter.server/src/main/scala/scouter/server/core/ProfilePreCore.scala index 3da592c18..d39d69192 100644 --- a/scouter.server/src/main/scala/scouter/server/core/ProfilePreCore.scala +++ b/scouter.server/src/main/scala/scouter/server/core/ProfilePreCore.scala @@ -20,16 +20,19 @@ package scouter.server.core import java.util.function.Consumer +import scouter.lang.TextTypes import scouter.lang.pack.{PackEnum, XLogDiscardTypes, XLogPack, XLogProfilePack, XLogProfilePack2, XLogTypes} import scouter.server.core.ProfileCore.queue import scouter.server.core.ProfilePreCore.canProcess import scouter.server.core.XLogPreCore.processDelayingChildren import scouter.server.core.cache.{ProfileDelayingCache, XLogDelayingCache} -import scouter.server.db.XLogProfileWR +import scouter.server.db.{TextRD, XLogProfileWR} import scouter.server.plugin.PlugInManager import scouter.server.util.ThreadScala import scouter.server.{Configure, Logger} -import scouter.util.{BytesUtil, RequestQueue} +import scouter.util.{BytesUtil, DateUtil, RequestQueue} + +import scala.util.Random object ProfilePreCore { @@ -90,11 +93,23 @@ object ProfilePreCore { } } + val rnd = new Random + private def processDelayingProfiles(pack2: XLogProfilePack2) = { val profileList = ProfileDelayingCache.instance.popDelayingChildren(pack2); - profileList.forEach(new Consumer[XLogProfilePack] { - override def accept(delayingPack: XLogProfilePack): Unit = { - if (pack2.discardType != XLogDiscardTypes.DISCARD_ALL) { + val delayingCount = profileList.size() + var cnt = 0 + + profileList.forEach(new Consumer[XLogProfilePack2] { + override def accept(delayingPack: XLogProfilePack2): Unit = { + if (pack2.discardType != XLogDiscardTypes.DISCARD_ALL) { //this condition is correct + //TODO remove logging + if (conf.log_test_rate > 0 && rnd.nextInt(100) < conf.log_test_rate) { + val serviceName = TextRD.getString(DateUtil.yyyymmdd(delayingPack.time), TextTypes.SERVICE, delayingPack.service) + val profileSize = if (delayingPack.profile == null) 0 else delayingPack.profile.length + Logger.println(s"${profileSize}\t[processDelaying][${cnt}/${delayingCount}] ${delayingPack.toDebugString(serviceName)}") + cnt += 1 + } ProfileCore.add(delayingPack) } } @@ -104,12 +119,15 @@ object ProfilePreCore { private def canProcess(pack: XLogProfilePack): Boolean = { if (pack.getPackType() == PackEnum.XLOG_PROFILE2) { val pack2 = pack.asInstanceOf[XLogProfilePack2]; - return (pack2.isDriving() + + val result = (pack2.isDriving() || pack2.ignoreGlobalConsequentSampling || (XLogTypes.isService(pack2.xType) && XLogDiscardTypes.isAliveProfile(pack2.discardType)) || XLogTypes.isZipkin(pack2.xType) || XLogDelayingCache.instance.isProcessedGxidWithProfile(pack2.gxid) || pack2.discardType == 0) + + return result } return true; } @@ -118,13 +136,31 @@ object ProfilePreCore { if (pack.getPackType() == PackEnum.XLOG_PROFILE2) { val pack2 = pack.asInstanceOf[XLogProfilePack2]; if (pack2.ignoreGlobalConsequentSampling) { + //TODO remove logging if (XLogDiscardTypes.isAliveProfile(pack2.discardType)) { + if (conf.log_test_rate > 0 && rnd.nextInt(100) < conf.log_test_rate) { + val serviceName = TextRD.getString(DateUtil.yyyymmdd(pack.time), TextTypes.SERVICE, pack.service) + val profileSize = if (pack.profile == null) 0 else pack.profile.length + Logger.println(s"${profileSize}\t[ProfileCoreCase1(IgnoreCons)] ${pack2.toDebugString(serviceName)}") + } ProfileCore.add(pack); } } else { + //TODO remove logging + if (conf.log_test_rate > 0 && rnd.nextInt(100) < conf.log_test_rate) { + val serviceName = TextRD.getString(DateUtil.yyyymmdd(pack.time), TextTypes.SERVICE, pack.service) + val profileSize = if (pack.profile == null) 0 else pack.profile.length + Logger.println(s"${profileSize}\t[ProfileCoreCase2] ${pack2.toDebugString(serviceName)}") + } ProfileCore.add(pack); } } else { + //TODO remove logging + if (conf.log_test_rate > 0 && rnd.nextInt(100) < conf.log_test_rate) { + val serviceName = TextRD.getString(DateUtil.yyyymmdd(pack.time), TextTypes.SERVICE, pack.service) + val profileSize = if (pack.profile == null) 0 else pack.profile.length + Logger.println(s"${profileSize}\t[ProfileCoreCase3(Old)] ${pack.toDebugString(serviceName)}") + } ProfileCore.add(pack); } } diff --git a/scouter.webapp/pom.xml b/scouter.webapp/pom.xml index a91966283..a773b76ce 100644 --- a/scouter.webapp/pom.xml +++ b/scouter.webapp/pom.xml @@ -5,7 +5,7 @@ io.github.scouter-project scouter-parent - 2.8.1 + 2.10.0 4.0.0