/* * 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.MeterAPI; import scouter.agent.netio.data.DataProxy; import scouter.agent.plugin.PluginHttpCallTrace; import scouter.agent.plugin.PluginHttpServiceTrace; import scouter.agent.proxy.IHttpClient; import scouter.agent.proxy.SpringRestTemplateHttpRequestFactory; import scouter.agent.summary.ServiceSummary; import scouter.agent.trace.api.ApiCallTraceHelper; import scouter.lang.step.ApiCallStep; import scouter.lang.step.ApiCallStep2; import scouter.lang.step.MessageStep; import scouter.lang.step.SocketStep; import scouter.util.*; import java.io.File; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.*; public class TraceApiCall { private static Object lock = new Object(); private static IntKeyLinkedMap<IHttpClient> restTemplateHttpRequests = new IntKeyLinkedMap<IHttpClient>().setMax(5); static { try { PluginHttpServiceTrace.class.getClass(); } catch (Throwable t) { } } public static class Stat { public TraceContext ctx; public Object req; public Object res; public Stat(TraceContext ctx, Object req, Object res) { this.ctx = ctx; this.req = req; this.res = res; } public Stat(TraceContext ctx) { this.ctx = ctx; } } public static void apiInfo(String className, String methodName, String methodDesc, Object _this, Object[] arg) { TraceContext ctx = TraceContextManager.getContext(); if (ctx != null && arg.length >= 2) { ctx.apicall_target = arg[0] + ":" + arg[1]; } } public static Object startApicall(String className, String methodName, String methodDesc, Object _this, Object[] arg) { try { TraceContext ctx = TraceContextManager.getContext(); if (ctx == null) { return null; } if (ctx.apicall_name != null) { return null; } // System.out.println("apicall start: " +ctx.apicall_name + // " target="+ctx.apicall_target); HookArgs hookPoint = new HookArgs(className, methodName, methodDesc, _this, arg); ApiCallStep step = ApiCallTraceHelper.start(ctx, hookPoint); if (step == null) return null; step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); if (ctx.profile_thread_cputime) { step.start_cpu = (int) (SysJMX.getCurrentThreadCPU() - ctx.startCpu); } ctx.profile.push(step); return new LocalContext(ctx, step, hookPoint); } catch (Throwable sss) { sss.printStackTrace(); } return null; } public static Object startApicall(String name, long apiTxid) { TraceContext ctx = TraceContextManager.getContext(); if (ctx == null) return null; if (ctx.apicall_name != null) { return null; } ApiCallStep step = new ApiCallStep(); step.txid = apiTxid; step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); if (ctx.profile_thread_cputime) { step.start_cpu = (int) (SysJMX.getCurrentThreadCPU() - ctx.startCpu); } ctx.profile.push(step); ctx.apicall_name = name; return new LocalContext(ctx, step); } public static void endApicall(Object stat, Object returnValue, Throwable thr) { if (stat == null) return; try { LocalContext lctx = (LocalContext) stat; TraceContext tctx = (TraceContext) lctx.context; ApiCallStep step = (ApiCallStep) lctx.stepSingle; // System.out.println("apicall end: " +tctx.apicall_name + // " target="+tctx.apicall_target); if (step.address == null) { step.address = tctx.apicall_target; } step.hash = DataProxy.sendApicall(tctx.apicall_name); tctx.apicall_name = null; tctx.apicall_target = null; tctx.lastApiCallStep = null; step.elapsed = (int) (System.currentTimeMillis() - tctx.startTime) - step.start_time; if (tctx.profile_thread_cputime) { step.cputime = (int) (SysJMX.getCurrentThreadCPU() - tctx.startCpu) - step.start_cpu; } tctx.apicall_count++; tctx.apicall_time += step.elapsed; if (thr != null) { String msg = thr.getMessage(); if(msg == null){ msg = thr.toString(); } Configure conf = Configure.getInstance(); if (conf.profile_fullstack_apicall_error_enabled) { StringBuffer sb = new StringBuffer(); sb.append(msg).append("\n"); ThreadUtil.getStackTrace(sb, thr, conf.profile_fullstack_max_lines); thr = thr.getCause(); while (thr != null) { sb.append("\nCause...\n"); ThreadUtil.getStackTrace(sb, thr, conf.profile_fullstack_max_lines); thr = thr.getCause(); } msg = sb.toString(); } step.error = DataProxy.sendError(msg); if (tctx.error == 0) { tctx.error = step.error; } ServiceSummary.getInstance().process(thr, step.error, tctx.serviceHash, tctx.txid, 0, step.hash); } if(step instanceof ApiCallStep2 && ((ApiCallStep2) step).async == 1) { //skip api metering } else { MeterAPI.getInstance().add(step.elapsed, step.error != 0); } ServiceSummary.getInstance().process(step); tctx.profile.pop(step); } catch (Throwable t) { t.printStackTrace(); } } public static Object startSocket(Object socketOrSocketChannel, SocketAddress addr) { if (!(addr instanceof InetSocketAddress)) return null; TraceContext ctx = TraceContextManager.getContext(); if (ctx == null) { if (Configure.getInstance().trace_background_socket_enabled) { InetSocketAddress inet = (InetSocketAddress) addr; InetAddress host = inet.getAddress(); int port = inet.getPort(); byte[] ipaddr = host == null ? null : host.getAddress(); SocketTable.add(ipaddr, port, 0, 0); } return null; } try { SocketStep step = new SocketStep(); step.start_time = (int) (System.currentTimeMillis() - ctx.startTime); if (ctx.profile_thread_cputime) { step.start_cpu = (int) (SysJMX.getCurrentThreadCPU() - ctx.startCpu); } InetSocketAddress inet = (InetSocketAddress) addr; InetAddress host = inet.getAddress(); int port = inet.getPort(); step.ipaddr = host == null ? null : host.getAddress(); step.port = port; return new LocalContext(ctx, step, socketOrSocketChannel); } catch (Throwable t) { Logger.println("A141", "socket trace error", t); return null; } } public static void endSocket(Object stat, Throwable thr) { if (stat == null) { return; } try { LocalContext lctx = (LocalContext) stat; TraceContext tctx = lctx.context; SocketStep step = (SocketStep) lctx.stepSingle; step.elapsed = (int) (System.currentTimeMillis() - tctx.startTime) - step.start_time; if (thr != null) { String msg = thr.toString(); step.error = DataProxy.sendError(msg); if (tctx.error == 0) { tctx.error = step.error; } ServiceSummary.getInstance().process(thr, step.error, tctx.serviceHash, tctx.txid, 0, 0); } tctx.profile.add(step); SocketTable.add(step.ipaddr, step.port, tctx.serviceHash, tctx.txid); Configure conf = Configure.getInstance(); if (conf.profile_socket_open_fullstack_enabled) { if (conf.profile_socket_open_fullstack_port == 0 || conf.profile_socket_open_fullstack_port == step.port) { tctx.profile.add(new MessageStep(step.start_time, ThreadUtil.getThreadStack())); } } } catch (Throwable t) { Logger.println("A142", "socket trace close error", t); } } public static void open(File file) { TraceContext ctx = TraceContextManager.getContext(); if (ctx != null) { MessageStep m = new MessageStep(); m.start_time = (int) (System.currentTimeMillis() - ctx.startTime); m.message = "FILE " + file.getName(); ctx.profile.add(m); } } public static final IHttpClient dummyHttpClient = new IHttpClient() { public String getURI(Object o) { return null; } public String getHost(Object o) { return null; } public String getHeader(Object o, String key) { return null; } public void addHeader(Object o, String key, String value) { } }; public static void endCreateSpringRestTemplateRequest(Object _this, Object oRtn) { TraceContext ctx = TraceContextManager.getContext(); if(ctx == null) return; if(ctx.lastApiCallStep == null) return; Configure conf = Configure.getInstance(); int key = System.identityHashCode(_this.getClass()); IHttpClient httpclient = restTemplateHttpRequests.get(key); if (httpclient == null) { synchronized (lock) { if (httpclient == null) { if (_this.getClass().getClassLoader() == null) { httpclient = dummyHttpClient; } else { Set<String> allSuperSet = getAllExtendedOrImplementedTypesRecursively(oRtn.getClass()); if (allSuperSet.contains("org.springframework.http.HttpRequest")) { //Spring 3.0 doesn't have the interface. HttpRequest is since 3.1 httpclient = SpringRestTemplateHttpRequestFactory.create(_this.getClass().getClassLoader()); } else { httpclient = dummyHttpClient; } } restTemplateHttpRequests.put(key, httpclient); } } } if (conf.trace_interservice_enabled) { try { if (ctx.gxid == 0) { ctx.gxid = ctx.txid; } ctx.lastApiCallStep.txid = KeyGen.next(); httpclient.addHeader(oRtn, conf._trace_interservice_gxid_header_key, Hexa32.toString32(ctx.gxid)); httpclient.addHeader(oRtn, conf._trace_interservice_caller_header_key, Hexa32.toString32(ctx.txid)); httpclient.addHeader(oRtn, conf._trace_interservice_callee_header_key, Hexa32.toString32(ctx.lastApiCallStep.txid)); httpclient.addHeader(oRtn, "scouter_caller_url", ctx.serviceName); httpclient.addHeader(oRtn, "scouter_caller_name", conf.getObjName()); httpclient.addHeader(oRtn, "scouter_thread_id", Long.toString(ctx.threadId)); PluginHttpCallTrace.call(ctx, httpclient, oRtn); } catch (Exception e) { } } } public static Set<String> getAllExtendedOrImplementedTypesRecursively(Class clazz) { List<String> res = new ArrayList<String>(); do { res.add(clazz.getName()); // First, add all the interfaces implemented by this class Class[] interfaces = clazz.getInterfaces(); if (interfaces.length > 0) { for(int i=0; i<interfaces.length; i++) { res.add(interfaces[i].getName()); } for (Class interfaze : interfaces) { res.addAll(getAllExtendedOrImplementedTypesRecursively(interfaze)); } } // Add the super class Class superClass = clazz.getSuperclass(); // Interfaces does not have java,lang.Object as superclass, they have null, so break the cycle and return if (superClass == null) { break; } // Now inspect the superclass clazz = superClass; } while (!"java.lang.Object".equals(clazz.getCanonicalName())); return new HashSet<String>(res); } }