OAL 解释器实现

OAL 解释器是基于 Antlr4 实现的,我们先来了解下 Antlr4

Antlr4 基本介绍

Antlr4 使用案例

参考Antlr4的使用简介这篇文章,我们实现了一个简单的案例:antlr案例:简单的计算器,下面来讲讲这个案例。

首先,装好ANTLR v4(IDEA插件)插件,这个之后验证语法树的时候会用到。

pom.xml 中配置 antlr4 的依赖和插件

<dependency><groupId>org.antlr</groupId><artifactId>antlr4-runtime</artifactId><version>4.7.1</version>
</dependency>
<plugin><groupId>org.antlr</groupId><artifactId>antlr4-maven-plugin</artifactId><version>${antlr.version}</version><executions><execution><id>antlr</id><goals><goal>antlr4</goal></goals></execution></executions>
</plugin>

src/main/antlr4/com/switchvov/antlr/demo/calc 目录下添加一个 Calc.g4 文件

grammar Calc;   //名称需要和文件名一致root : expr EOF;   //解决问题: no viable alternative at input '<EOF>'expr: expr '+' expr     #add   //标签会生成对应访问方法方便我们实现调用逻辑编写| expr '-' expr     #sub| INT               #int;INT : [0-9]+                   //定义整数;WS : [ \r\n\t]+ -> skip      //跳过空白类字符;

执行一下: mvn compile -Dmaven.test.skip=true ,在 target/generated-sources/antlr4 会生成相应的 Java 代码。

使用方式默认是监听器模式,也可以配置成访问者模式。

监听器模式:主要借助了 ParseTreeWalker 这样一个类,相当于是一个 hook ,每经过一个树的节点,便会触发对应节点的方法。好处就算是比较方便,但是灵活性不够,不能够自主性的调用任意节点进行使用。

访问者模式:将每个数据的节点类型高度抽象出来够,根据你传入的上下文类型来判断你想要访问的是哪个节点,触发对应的方法

PS:结论,简单语法监听器模式就可以了,如果语法比较灵活可以考虑使用访问者模式。

antlr4
├── Calc.tokens
├── CalcLexer.tokens
└── com└── switchvov└── antlr└── demo└── calc├── Calc.interp├── CalcBaseListener.java # 监听模式下生成的监听器基类,实现类监听器接口,通过继承该类可以实现相应的功能├── CalcLexer.interp├── CalcLexer.java  # 词法解析器├── CalcListener.java # 监听模式下生成的监听器接口└── CalcParser.java # 语法解析器

继承 com.switchvov.antlr.demo.calc.CalcBaseListener ,实现计算器相应功能

package com.switchvov.antlr.demo.calc;import java.util.ArrayDeque;
import java.util.Deque;/*** @author switch* @since 2021/6/30*/
public class CalcExecuteListener extends CalcBaseListener {Deque<Integer> queue = new ArrayDeque<>(16);@Overridepublic void exitInt(CalcParser.IntContext ctx) {queue.add(Integer.parseInt(ctx.INT().getText()));}@Overridepublic void exitAdd(CalcParser.AddContext ctx) {int r = queue.pop();int l = queue.pop();queue.add(l + r);}@Overridepublic void exitSub(CalcParser.SubContext ctx) {int r = queue.pop();int l = queue.pop();queue.add(l - r);}public int result() {return queue.pop();}
}

测试一下

package com.switchvov.antlr.demo.calc;import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.junit.Test;/*** @author switch* @since 2021/6/30*/
public class CalcTest {public static int exec(String input) {// 读入字符串CodePointCharStream cs = CharStreams.fromString(input);// 词法解析CalcLexer lexer = new CalcLexer(cs);CommonTokenStream tokens = new CommonTokenStream(lexer);// 语法解析CalcParser parser = new CalcParser(tokens);// 监听器触发获取执行结果ParseTree tree = parser.expr();ParseTreeWalker walker = new ParseTreeWalker();CalcExecuteListener listener = new CalcExecuteListener();walker.walk(listener, tree);return listener.result();}@Testpublic void testCalc() {String input = "1+2";// 输出结果:3System.out.println(exec(input));}}

Antlr4 IDEA 插件使用

Calc.g4 语法定义文件中,鼠标右击可以选择 Test Rule root ,然后在 ANTLR Preview 的输入框中填入 1 + 2 就可以校验语法文件是否 OK ,并且也可以看到相应的语法树

Antlr4 在 Skywalking 的应用

通过“ Antlr4 基本介绍”一节,基本上对 Antlr4 使用有了个大概的认识。下面来看看 Skywalking 中 Antlr4 是如何使用的。

词法定义

oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALLexer.g4 文件中,我们能看到 OAL 的词法定义

// Observability Analysis Language lexer
lexer grammar OALLexer;@Header {package org.apache.skywalking.oal.rt.grammar;}// KeywordsFROM: 'from';
FILTER: 'filter';
DISABLE: 'disable';
SRC_ALL: 'All';
SRC_SERVICE: 'Service';
SRC_SERVICE_INSTANCE: 'ServiceInstance';
SRC_ENDPOINT: 'Endpoint';
SRC_SERVICE_RELATION: 'ServiceRelation';
SRC_SERVICE_INSTANCE_RELATION: 'ServiceInstanceRelation';
SRC_ENDPOINT_RELATION: 'EndpointRelation';
SRC_SERVICE_INSTANCE_JVM_CPU: 'ServiceInstanceJVMCPU';
SRC_SERVICE_INSTANCE_JVM_MEMORY: 'ServiceInstanceJVMMemory';
SRC_SERVICE_INSTANCE_JVM_MEMORY_POOL: 'ServiceInstanceJVMMemoryPool';
SRC_SERVICE_INSTANCE_JVM_GC: 'ServiceInstanceJVMGC';
SRC_SERVICE_INSTANCE_JVM_THREAD: 'ServiceInstanceJVMThread';
SRC_SERVICE_INSTANCE_JVM_CLASS:'ServiceInstanceJVMClass';
SRC_DATABASE_ACCESS: 'DatabaseAccess';
SRC_SERVICE_INSTANCE_CLR_CPU: 'ServiceInstanceCLRCPU';
SRC_SERVICE_INSTANCE_CLR_GC: 'ServiceInstanceCLRGC';
SRC_SERVICE_INSTANCE_CLR_THREAD: 'ServiceInstanceCLRThread';
SRC_ENVOY_INSTANCE_METRIC: 'EnvoyInstanceMetric';// Browser keywords
SRC_BROWSER_APP_PERF: 'BrowserAppPerf';
SRC_BROWSER_APP_PAGE_PERF: 'BrowserAppPagePerf';
SRC_BROWSER_APP_SINGLE_VERSION_PERF: 'BrowserAppSingleVersionPerf';
SRC_BROWSER_APP_TRAFFIC: 'BrowserAppTraffic';
SRC_BROWSER_APP_PAGE_TRAFFIC: 'BrowserAppPageTraffic';
SRC_BROWSER_APP_SINGLE_VERSION_TRAFFIC: 'BrowserAppSingleVersionTraffic';// Constructors symbolsDOT:                                 '.';
LR_BRACKET:                          '(';
RR_BRACKET:                          ')';
LS_BRACKET:                          '[';
RS_BRACKET:                          ']';
COMMA:                               ',';
SEMI:                                ';';
EQUAL:                               '=';
DUALEQUALS:                          '==';
ALL:                                 '*';
GREATER:                             '>';
LESS:                                '<';
GREATER_EQUAL:                       '>=';
LESS_EQUAL:                          '<=';
NOT_EQUAL:                           '!=';
LIKE:                                'like';
IN:                                  'in';
CONTAIN:                            'contain';
NOT_CONTAIN:                        'not contain';// LiteralsBOOL_LITERAL:       'true'|       'false';NUMBER_LITERAL :   Digits+;CHAR_LITERAL:       '\'' (~['\\\r\n] | EscapeSequence) '\'';STRING_LITERAL:     '"' (~["\\\r\n] | EscapeSequence)* '"';DelimitedComment: '/*' ( DelimitedComment | . )*? '*/'-> channel(HIDDEN);LineComment: '//' ~[\u000A\u000D]*-> channel(HIDDEN);SPACE:                               [ \t\r\n]+    -> channel(HIDDEN);// IdentifiersIDENTIFIER:         Letter LetterOrDigit*;// Fragment rulesfragment EscapeSequence: '\\' [btnfr"'\\]| '\\' ([0-3]? [0-7])? [0-7]| '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit;fragment HexDigits: HexDigit ((HexDigit | '_')* HexDigit)?;fragment HexDigit: [0-9a-fA-F];fragment Digits: [0-9] ([0-9_]* [0-9])?;fragment LetterOrDigit: Letter| [0-9];fragment Letter: [a-zA-Z$_] // these are the "java letters" below 0x7F| ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate| [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF;

语法定义

oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALParser.g4 文件中,我们能看到 OAL 的语法定义

parser grammar OALParser;@Header {package org.apache.skywalking.oal.rt.grammar;}options { tokenVocab=OALLexer; }// Top Level Descriptionroot: (aggregationStatement | disableStatement)*;aggregationStatement: variable (SPACE)? EQUAL (SPACE)? metricStatement DelimitedComment? LineComment? (SEMI|EOF);disableStatement: DISABLE LR_BRACKET disableSource RR_BRACKET DelimitedComment? LineComment? (SEMI|EOF);metricStatement: FROM LR_BRACKET source (sourceAttributeStmt+) RR_BRACKET (filterStatement+)? DOT aggregateFunction;filterStatement: DOT FILTER LR_BRACKET filterExpression RR_BRACKET;filterExpression: expression;source: SRC_ALL | SRC_SERVICE | SRC_DATABASE_ACCESS | SRC_SERVICE_INSTANCE | SRC_ENDPOINT |SRC_SERVICE_RELATION | SRC_SERVICE_INSTANCE_RELATION | SRC_ENDPOINT_RELATION |SRC_SERVICE_INSTANCE_JVM_CPU | SRC_SERVICE_INSTANCE_JVM_MEMORY | SRC_SERVICE_INSTANCE_JVM_MEMORY_POOL | SRC_SERVICE_INSTANCE_JVM_GC | SRC_SERVICE_INSTANCE_JVM_THREAD | SRC_SERVICE_INSTANCE_JVM_CLASS |// JVM source of service instanceSRC_SERVICE_INSTANCE_CLR_CPU | SRC_SERVICE_INSTANCE_CLR_GC | SRC_SERVICE_INSTANCE_CLR_THREAD |SRC_ENVOY_INSTANCE_METRIC |SRC_BROWSER_APP_PERF | SRC_BROWSER_APP_PAGE_PERF | SRC_BROWSER_APP_SINGLE_VERSION_PERF |SRC_BROWSER_APP_TRAFFIC | SRC_BROWSER_APP_PAGE_TRAFFIC | SRC_BROWSER_APP_SINGLE_VERSION_TRAFFIC;disableSource: IDENTIFIER;sourceAttributeStmt: DOT sourceAttribute;sourceAttribute: IDENTIFIER | ALL;variable: IDENTIFIER;aggregateFunction: functionName LR_BRACKET ((funcParamExpression (COMMA funcParamExpression)?) | (literalExpression (COMMA literalExpression)?))? RR_BRACKET;functionName: IDENTIFIER;funcParamExpression: expression;literalExpression: BOOL_LITERAL | NUMBER_LITERAL | IDENTIFIER;expression: booleanMatch | stringMatch | greaterMatch | lessMatch | greaterEqualMatch | lessEqualMatch | notEqualMatch | booleanNotEqualMatch | likeMatch | inMatch | containMatch | notContainMatch;containMatch: conditionAttributeStmt CONTAIN stringConditionValue;notContainMatch: conditionAttributeStmt NOT_CONTAIN stringConditionValue;booleanMatch: conditionAttributeStmt DUALEQUALS booleanConditionValue;stringMatch:  conditionAttributeStmt DUALEQUALS (stringConditionValue | enumConditionValue);greaterMatch:  conditionAttributeStmt GREATER numberConditionValue;lessMatch:  conditionAttributeStmt LESS numberConditionValue;greaterEqualMatch:  conditionAttributeStmt GREATER_EQUAL numberConditionValue;lessEqualMatch:  conditionAttributeStmt LESS_EQUAL numberConditionValue;booleanNotEqualMatch:  conditionAttributeStmt NOT_EQUAL booleanConditionValue;notEqualMatch:  conditionAttributeStmt NOT_EQUAL (numberConditionValue | stringConditionValue | enumConditionValue);likeMatch:  conditionAttributeStmt LIKE stringConditionValue;inMatch:  conditionAttributeStmt IN multiConditionValue;multiConditionValue: LS_BRACKET (numberConditionValue ((COMMA numberConditionValue)*) | stringConditionValue ((COMMA stringConditionValue)*) | enumConditionValue ((COMMA enumConditionValue)*)) RS_BRACKET;conditionAttributeStmt: conditionAttribute ((DOT conditionAttribute)*);conditionAttribute: IDENTIFIER;booleanConditionValue: BOOL_LITERAL;stringConditionValue: STRING_LITERAL;enumConditionValue: IDENTIFIER DOT IDENTIFIER;numberConditionValue: NUMBER_LITERAL;

Antlr4 生成 Java 代码

oap-server/oal-grammar 下执行 mvn compile -Dmaven.test.skip=true 会在 oap-server/oal-grammar/target/generated-sources/antlr4 目录下生成相应的 Java 代码

.
├── OALLexer.tokens
├── OALParser.tokens
└── org└── apache└── skywalking└── oal└── rt└── grammar├── OALLexer.interp├── OALLexer.java # 词法解析器├── OALParser.interp├── OALParser.java # 语法解析器├── OALParserBaseListener.java # 监听器└── OALParserListener.java

Skywalking 的使用

通过“ Antlr4 使用案例”一节,可以知道 Antlr4 有两种功能实现方式:监听器或者访问器。

通过“ Antlr4 生成 Java 代码”一节,知道 Skywalking 使用的是监听器模式。

Skywalking 关于 OAL 的相应的代码都在 oap-server/oal-rt 模块中。

org.apache.skywalking.oal.rt.grammar.OALParserBaseListener 的继承类坐标是 org.apache.skywalking.oal.rt.parser.OALListener

package org.apache.skywalking.oal.rt.parser;import java.util.Arrays;
import java.util.List;
import org.antlr.v4.runtime.misc.NotNull;
import org.apache.skywalking.oal.rt.grammar.OALParser;
import org.apache.skywalking.oal.rt.grammar.OALParserBaseListener;
import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;public class OALListener extends OALParserBaseListener {private List<AnalysisResult> results;private AnalysisResult current;private DisableCollection collection;private ConditionExpression conditionExpression;private final String sourcePackage;public OALListener(OALScripts scripts, String sourcePackage) {this.results = scripts.getMetricsStmts();this.collection = scripts.getDisableCollection();this.sourcePackage = sourcePackage;}@Overridepublic void enterAggregationStatement(@NotNull OALParser.AggregationStatementContext ctx) {current = new AnalysisResult();}@Overridepublic void exitAggregationStatement(@NotNull OALParser.AggregationStatementContext ctx) {DeepAnalysis deepAnalysis = new DeepAnalysis();results.add(deepAnalysis.analysis(current));current = null;}@Overridepublic void enterSource(OALParser.SourceContext ctx) {current.setSourceName(ctx.getText());current.setSourceScopeId(DefaultScopeDefine.valueOf(metricsNameFormat(ctx.getText())));}@Overridepublic void enterSourceAttribute(OALParser.SourceAttributeContext ctx) {current.getSourceAttribute().add(ctx.getText());}@Overridepublic void enterVariable(OALParser.VariableContext ctx) {}@Overridepublic void exitVariable(OALParser.VariableContext ctx) {current.setVarName(ctx.getText());current.setMetricsName(metricsNameFormat(ctx.getText()));current.setTableName(ctx.getText().toLowerCase());}@Overridepublic void enterFunctionName(OALParser.FunctionNameContext ctx) {current.setAggregationFunctionName(ctx.getText());}@Overridepublic void enterFilterStatement(OALParser.FilterStatementContext ctx) {conditionExpression = new ConditionExpression();}@Overridepublic void exitFilterStatement(OALParser.FilterStatementContext ctx) {current.addFilterExpressionsParserResult(conditionExpression);conditionExpression = null;}@Overridepublic void enterFuncParamExpression(OALParser.FuncParamExpressionContext ctx) {conditionExpression = new ConditionExpression();}@Overridepublic void exitFuncParamExpression(OALParser.FuncParamExpressionContext ctx) {current.addFuncConditionExpression(conditionExpression);conditionExpression = null;}/// Expression@Overridepublic void enterConditionAttribute(OALParser.ConditionAttributeContext ctx) {conditionExpression.getAttributes().add(ctx.getText());}@Overridepublic void enterBooleanMatch(OALParser.BooleanMatchContext ctx) {conditionExpression.setExpressionType("booleanMatch");}@Overridepublic void enterStringMatch(OALParser.StringMatchContext ctx) {conditionExpression.setExpressionType("stringMatch");}@Overridepublic void enterGreaterMatch(OALParser.GreaterMatchContext ctx) {conditionExpression.setExpressionType("greaterMatch");}@Overridepublic void enterGreaterEqualMatch(OALParser.GreaterEqualMatchContext ctx) {conditionExpression.setExpressionType("greaterEqualMatch");}@Overridepublic void enterLessMatch(OALParser.LessMatchContext ctx) {conditionExpression.setExpressionType("lessMatch");}@Overridepublic void enterLessEqualMatch(OALParser.LessEqualMatchContext ctx) {conditionExpression.setExpressionType("lessEqualMatch");}@Overridepublic void enterNotEqualMatch(final OALParser.NotEqualMatchContext ctx) {conditionExpression.setExpressionType("notEqualMatch");}@Overridepublic void enterBooleanNotEqualMatch(final OALParser.BooleanNotEqualMatchContext ctx) {conditionExpression.setExpressionType("booleanNotEqualMatch");}@Overridepublic void enterLikeMatch(final OALParser.LikeMatchContext ctx) {conditionExpression.setExpressionType("likeMatch");}@Overridepublic void enterContainMatch(final OALParser.ContainMatchContext ctx) {conditionExpression.setExpressionType("containMatch");}@Overridepublic void enterNotContainMatch(final OALParser.NotContainMatchContext ctx) {conditionExpression.setExpressionType("notContainMatch");}@Overridepublic void enterInMatch(final OALParser.InMatchContext ctx) {conditionExpression.setExpressionType("inMatch");}@Overridepublic void enterMultiConditionValue(final OALParser.MultiConditionValueContext ctx) {conditionExpression.enterMultiConditionValue();}@Overridepublic void exitMultiConditionValue(final OALParser.MultiConditionValueContext ctx) {conditionExpression.exitMultiConditionValue();}@Overridepublic void enterBooleanConditionValue(OALParser.BooleanConditionValueContext ctx) {enterConditionValue(ctx.getText());}@Overridepublic void enterStringConditionValue(OALParser.StringConditionValueContext ctx) {enterConditionValue(ctx.getText());}@Overridepublic void enterEnumConditionValue(OALParser.EnumConditionValueContext ctx) {enterConditionValue(ctx.getText());}@Overridepublic void enterNumberConditionValue(OALParser.NumberConditionValueContext ctx) {conditionExpression.isNumber();enterConditionValue(ctx.getText());}private void enterConditionValue(String value) {if (value.split("\\.").length == 2 && !value.startsWith("\"")) {// Value is an enum.value = sourcePackage + value;}conditionExpression.addValue(value);}/// Expression end.@Overridepublic void enterLiteralExpression(OALParser.LiteralExpressionContext ctx) {if (ctx.IDENTIFIER() == null) {current.addFuncArg(new Argument(EntryMethod.LITERAL_TYPE, Arrays.asList(ctx.getText())));return;}current.addFuncArg(new Argument(EntryMethod.IDENTIFIER_TYPE, Arrays.asList(ctx.getText().split("\\."))));}private String metricsNameFormat(String source) {source = firstLetterUpper(source);int idx;while ((idx = source.indexOf("_")) > -1) {source = source.substring(0, idx) + firstLetterUpper(source.substring(idx + 1));}return source;}/*** Disable source*/@Overridepublic void enterDisableSource(OALParser.DisableSourceContext ctx) {collection.add(ctx.getText());}private String firstLetterUpper(String source) {return source.substring(0, 1).toUpperCase() + source.substring(1);}
}

简单来说,就是通过监听器封装了个 org.apache.skywalking.oal.rt.parser.OALScripts 对象

package org.apache.skywalking.oal.rt.parser;import java.util.LinkedList;
import java.util.List;
import lombok.Getter;@Getter
public class OALScripts {// 解析出来的分析结果集合private List<AnalysisResult> metricsStmts;// 禁用表达式集合private DisableCollection disableCollection;public OALScripts() {metricsStmts = new LinkedList<>();disableCollection = new DisableCollection();}
}

org.apache.skywalking.oal.rt.parser.ScriptParser 类读取 oal 文件,使用 Antlr 生成的 Java 类进行解析

package org.apache.skywalking.oal.rt.parser;import java.io.IOException;
import java.io.Reader;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.apache.skywalking.oal.rt.grammar.OALLexer;
import org.apache.skywalking.oal.rt.grammar.OALParser;/*** Script reader and parser.*/
public class ScriptParser {private OALLexer lexer;private String sourcePackage;private ScriptParser() {}public static ScriptParser createFromFile(Reader scriptReader, String sourcePackage) throws IOException {ScriptParser parser = new ScriptParser();parser.lexer = new OALLexer(CharStreams.fromReader(scriptReader));parser.sourcePackage = sourcePackage;return parser;}public static ScriptParser createFromScriptText(String script, String sourcePackage) throws IOException {ScriptParser parser = new ScriptParser();parser.lexer = new OALLexer(CharStreams.fromString(script));parser.sourcePackage = sourcePackage;return parser;}public OALScripts parse() throws IOException {OALScripts scripts = new OALScripts();CommonTokenStream tokens = new CommonTokenStream(lexer);OALParser parser = new OALParser(tokens);ParseTree tree = parser.root();ParseTreeWalker walker = new ParseTreeWalker();walker.walk(new OALListener(scripts, sourcePackage), tree);return scripts;}public void close() {}
}

参考文档

  • ANTLR官网
  • antlr4 GitHub
  • antlr4 语法案例
  • Antlr4的使用简介
  • antlr案例:简单的计算器
  • 某小伙的Antlr4学习笔记
  • ANTLR v4(IDEA插件)

Skywalking-07:OAL原理——解释器实现相关推荐

  1. Skywalking-09:OAL原理——如何通过动态生成的Class类保存数据

    OAL 如何通过动态生成的 Class 类,保存数据 前置工作 OAL 如何将动态生成的 SourceDispatcher 添加到 DispatcherManager // org.apache.sk ...

  2. Skywalking-08:OAL原理——如何动态生成Class类

    OAL 如何动态生成 Class 类 代码入口 在 org.apache.skywalking.oal.rt.OALRuntime#start 方法 public void start(ClassLo ...

  3. SkyWalking Agent 实现原理

    微内核架构 SkyWalking Agent 采用了微内核架构(Microkernel Architecture),那什么是微内核架构呢?微内核架构也被称为插件化架构(Plug-in Architec ...

  4. 分布式链路追踪 之 Skywalking 设计理念核心原理

    摘要 对大型分布式系统进行监视,可视化和故障排除是一项重大挑战.当今使用的一种常见工具是分布式跟踪系统(例如Google Dapper)[1],它基于跟踪数据检测拓扑和度量.当今拓扑检测的一大局限性在 ...

  5. 微服务链路追踪SkyWalking第十一课 OAL详解实战

    第31讲:OAL 语言,原来定义创造一门新语言如此轻松(上) 在前文介绍 Metrics 实现以及对应的 DIspatcher 实现的时候,会发现有一部分实现类位于 generated-analysi ...

  6. Skywalking-06:OAL基础

    OAL 基础知识 基本介绍 OAL(Observability Analysis Language) 是一门用来分析流式数据的语言. 因为 OAL 聚焦于度量 Service . Service In ...

  7. 微服务链路追踪SkyWalking第一课 SkyWalking简介

    开篇词:从剖析 SkyWalking 源码到吃透 APM 核心知识 你好,我是你的 SkyWalking 老师徐郡明,网名吴小胖,你也可以叫我胖哥.进入互联网行业工作多年,主要从事基础组件开发相关的工 ...

  8. 【编译原理】实现一个计算器

    实现一个计算器,包括加减,乘除,括号,错误提示,id等,相当于一个小型的解释器. 其中重要的思想是 把字符转换成token 递归解析 //编译原理实现计算器 #include<bits/stdc ...

  9. skywalking搭建与使用

    前言 在分布式环境中,对于服务的监控与链路追踪变得越来越重要,简单来说,相比单体应用,分布式环境下的服务链路变得更长,问题定位.排查与解决也更加复杂,因此使用一款好的服务监控工具可以为Java开发人员 ...

最新文章

  1. 关于子网划分—为什么全0全1子网号不能使用
  2. 【CV秋季划】深度学习换脸算法视频更新
  3. android trace文件分析ANR
  4. python打包成exe_【Python】使用pyinstaller打包成exe文件时可以显示图片的方法
  5. php 比较2个数组,比较2个数组中的2个数组值Php
  6. 《MySQL——加锁规则(待补全,有些没看懂)》
  7. python插入排序算法详解-面试常考
  8. 中文的括号和英文的括号区别_家庭教育的困惑 (数学,中/英文)
  9. 问题解决:Unlink of the file ‘.git/objects/pack/….idx’ failed. Should I try again?
  10. Windows10键盘快捷键大全
  11. mx250 计算能力_mx250显卡性能怎么样,mx250显卡性能相当于GTX多少
  12. 【快速入门大数据】hadoop和它的hdfs、yarn、mapreduce
  13. 密码库LibTomCrypt学习记录——(2.25)分组密码算法的工作模式——EAX加密认证模式
  14. JZOJ 1266. 玉米田
  15. PAT - 浙江大学计算机程序设计能力考试简介
  16. Win10使用FTP实现手机访问电脑FTP服务
  17. 两台电脑通过网线直接相连,用以共享文件夹
  18. Windows驱动_WSK驱动之二WSK的操作
  19. 测试通过与失败的标准
  20. Vanilla JS速度超越了vue/react/svelte---kalrry

热门文章

  1. Spring Cloud Consul 基础使用介绍
  2. 通过Gogs部署git仓库
  3. 【asp.net core 系列】14 .net core 中的IOC
  4. Yarn 报错:error Couldn‘t publish package: “https://registry.yarnpkg.com/ 。。。Forbidden“
  5. dnslog-server的概念
  6. 如何使用Dependabot保持环境最新
  7. php项目数据库控制器代码_如何为大型代码库组织Express控制器
  8. 机器学习:算法视角pdf_何时使用不同的机器学习算法:简单指南
  9. 121_Power Query之R.Execute的read.xlsxODBC
  10. vba 判断控件有无_6小时,写了一篇适合Excel小白学的VBA入门教程