diff --git a/.coding/Jenkinsfile b/.coding/Jenkinsfile
new file mode 100644
index 0000000000..10cb7ff6b8
--- /dev/null
+++ b/.coding/Jenkinsfile
@@ -0,0 +1,63 @@
+pipeline {
+ agent any
+ environment {
+ MAVEN_SNAPSHOTS_NAME = "maven-snapshots"
+ MAVEN_SNAPSHOTS_ID = "${CCI_CURRENT_TEAM}-${PROJECT_NAME}-${MAVEN_SNAPSHOTS_NAME}"
+ MAVEN_SNAPSHOTS_URL = "${CCI_CURRENT_WEB_PROTOCOL}://${CCI_CURRENT_TEAM}-maven.pkg.${CCI_CURRENT_DOMAIN}/repository/${PROJECT_NAME}/${MAVEN_SNAPSHOTS_NAME}/"
+
+ MAVEN_RELEASES_NAME = "maven-releases"
+ MAVEN_RELEASES_ID = "${CCI_CURRENT_TEAM}-${PROJECT_NAME}-${MAVEN_RELEASES_NAME}"
+ MAVEN_RELEASES_URL = "${CCI_CURRENT_WEB_PROTOCOL}://${CCI_CURRENT_TEAM}-maven.pkg.${CCI_CURRENT_DOMAIN}/repository/${PROJECT_NAME}/${MAVEN_RELEASES_NAME}/"
+ }
+ stages {
+ stage('检出') {
+ steps {
+ checkout([$class: 'GitSCM',
+ branches: [[name: GIT_BUILD_REF]],
+ userRemoteConfigs: [[
+ url: GIT_REPO_URL,
+ credentialsId: CREDENTIALS_ID
+ ]]])
+ }
+ }
+ stage('编译') {
+ steps {
+ script {
+ if (env.TAG_NAME ==~ /.*/ ) {
+ ARTIFACT_VERSION = "${env.TAG_NAME}"
+ } else if (env.MR_SOURCE_BRANCH ==~ /.*/ ) {
+ ARTIFACT_VERSION = "${env.MR_RESOURCE_ID}-${env.GIT_COMMIT_SHORT}"
+ } else {
+ ARTIFACT_VERSION = "${env.BRANCH_NAME.replace('/', '-')}-${env.GIT_COMMIT_SHORT}"
+ }
+ }
+ withCredentials([
+ usernamePassword(
+ credentialsId: env.MAVEN_RELEASES,
+ usernameVariable: 'MAVEN_RELEASES_USERNAME',
+ passwordVariable: 'MAVEN_RELEASES_PASSWORD'
+ ),
+ usernamePassword(
+ credentialsId: env.MAVEN_SNAPSHOTS,
+ usernameVariable: 'MAVEN_SNAPSHOTS_USERNAME',
+ passwordVariable: 'MAVEN_SNAPSHOTS_PASSWORD'
+ )
+ ]) {
+ withEnv([
+ "ARTIFACT_VERSION=${ARTIFACT_VERSION}",
+ "MAVEN_RELEASES_ID=${MAVEN_RELEASES_ID}",
+ "MAVEN_RELEASES_URL=${MAVEN_RELEASES_URL}",
+ "MAVEN_RELEASES_USERNAME=${MAVEN_RELEASES_USERNAME}",
+ "MAVEN_RELEASES_PASSWORD=${MAVEN_RELEASES_PASSWORD}",
+ "MAVEN_SNAPSHOTS_ID=${MAVEN_SNAPSHOTS_ID}",
+ "MAVEN_SNAPSHOTS_URL=${MAVEN_SNAPSHOTS_URL}",
+ "MAVEN_SNAPSHOTS_USERNAME=${MAVEN_SNAPSHOTS_USERNAME}",
+ "MAVEN_SNAPSHOTS_PASSWORD=${MAVEN_SNAPSHOTS_PASSWORD}"
+ ]) {
+ sh 'mvn -T 4C -Pcoding versions:set -DnewVersion=${ARTIFACT_VERSION} package -DskipTests -s ./.coding/settings.xml'
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/.coding/settings.xml b/.coding/settings.xml
new file mode 100644
index 0000000000..8a8af285c3
--- /dev/null
+++ b/.coding/settings.xml
@@ -0,0 +1,83 @@
+
+
+
+
+ nexus-tencentyun
+ !mengxiangge-github-snapshots
+ Nexus tencentyun
+ http://mirrors.cloud.tencent.com/nexus/repository/maven-public/
+
+
+
+
+ ${env.MAVEN_RELEASES_ID}
+ ${env.MAVEN_RELEASES_USERNAME}
+ ${env.MAVEN_RELEASES_PASSWORD}
+
+
+ ${env.MAVEN_SNAPSHOTS_ID}
+ ${env.MAVEN_SNAPSHOTS_USERNAME}
+ ${env.MAVEN_SNAPSHOTS_PASSWORD}
+
+
+
+
+ coding
+
+
+ ${env.MAVEN_RELEASES_ID}::default::${env.MAVEN_RELEASES_URL}
+
+
+ ${env.MAVEN_SNAPSHOTS_ID}::default::${env.MAVEN_SNAPSHOTS_URL}
+
+
+
+
+ ${env.MAVEN_RELEASES_ID}
+ ${env.MAVEN_RELEASES_URL}
+
+ true
+
+
+ false
+
+
+
+ ${env.MAVEN_SNAPSHOTS_ID}
+ ${env.MAVEN_SNAPSHOTS_URL}
+
+ false
+
+
+ true
+
+
+
+
+
+ ${env.MAVEN_RELEASES_ID}
+ ${env.MAVEN_RELEASES_URL}
+
+ true
+
+
+ false
+
+
+
+ ${env.MAVEN_SNAPSHOTS_ID}
+ ${env.MAVEN_SNAPSHOTS_URL}
+
+ false
+
+
+ true
+
+
+
+
+
+
+ coding
+
+
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000..eb5a316cbd
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+target
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..8f8a36b897
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = 4
+max_line_length = 120
+
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{yaml,yml,bat}]
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[Jenkinsfile]
+indent_size = 2
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index 2ea54b5283..1fd53bace0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,148 @@
-*.js linguist-language=java
-*.css linguist-language=java
-*.html linguist-language=java
\ No newline at end of file
+* text=auto
+
+*.bat text eol=crlf
+*.coffee text
+*.css text
+*.cql text
+*.df text
+*.ejs text
+*.html text
+*.java text
+*.js text
+*.json text
+*.less text
+*.properties text
+*.sass text
+*.scss text
+*.sh text
+*.sql text
+*.txt text
+*.ts text
+*.xml text
+*.yaml text
+*.yml text
+
+# Documents
+*.doc diff=astextplain
+*.DOC diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot diff=astextplain
+*.DOT diff=astextplain
+*.pdf diff=astextplain
+*.PDF diff=astextplain
+*.rtf diff=astextplain
+*.RTF diff=astextplain
+*.markdown text
+*.md text
+*.adoc text
+*.textile text
+*.mustache text
+*.csv text
+*.tab text
+*.tsv text
+*.txt text
+AUTHORS text
+CHANGELOG text
+CHANGES text
+CONTRIBUTING text
+COPYING text
+copyright text
+*COPYRIGHT* text
+INSTALL text
+license text
+LICENSE text
+NEWS text
+readme text
+*README* text
+TODO text
+
+# Graphics
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+*.tif binary
+*.tiff binary
+*.ico binary
+*.svg binary
+#*.svg text
+*.eps binary
+*.class binary
+*.jar binary
+*.war binary
+
+## LINTERS
+.csslintrc text
+.eslintrc text
+.jscsrc text
+.jshintrc text
+.jshintignore text
+.stylelintrc text
+
+## CONFIGS
+*.bowerrc text
+*.conf text
+*.config text
+.editorconfig text
+.gitattributes text
+.gitconfig text
+.gitignore text
+.htaccess text
+*.npmignore text
+
+## HEROKU
+Procfile text
+.slugignore text
+
+## AUDIO
+*.kar binary
+*.m4a binary
+*.mid binary
+*.midi binary
+*.mp3 binary
+*.ogg binary
+*.ra binary
+
+## VIDEO
+*.3gpp binary
+*.3gp binary
+*.as binary
+*.asf binary
+*.asx binary
+*.fla binary
+*.flv binary
+*.m4v binary
+*.mng binary
+*.mov binary
+*.mp4 binary
+*.mpeg binary
+*.mpg binary
+*.swc binary
+*.swf binary
+*.webm binary
+
+## ARCHIVES
+*.7z binary
+*.gz binary
+*.rar binary
+*.tar binary
+*.zip binary
+
+## FONTS
+*.ttf binary
+*.eot binary
+*.otf binary
+*.woff binary
+*.woff2 binary
+
+## Custom
+*.md linguist-language=Java
+*.yml linguist-language=Java
+*.html linguist-language=Java
+*.js linguist-language=Java
+*.xml linguist-language=Java
+*.css linguist-language=Java
+*.sql linguist-language=Java
+*.uml linguist-language=Java
+*.cmd linguist-language=Java
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index dd84ea7824..0000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
-
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index bbcbbe7d61..0000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index 445454dada..0000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Build
-
-on:
- workflow_dispatch:
- inputs:
- logLevel:
- description: 'Log level'
- required: false
- default: 'warning'
- type: choice
- options:
- - info
- - warning
- - debug
- push:
- branches:
- - master
-
-jobs:
- build:
- runs-on: ubuntu-latest
- env:
- HUB: meituaninc
- IMAGE: cat
- TAG: ${{ github.sha }}
- steps:
- - uses: actions/checkout@v3
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v1
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
- - name: Build the Docker image
- run: docker buildx create --use --name cat-bulder --driver docker-container &&
- docker buildx build --load
- -t $HUB/$IMAGE:$TAG -t $HUB/$IMAGE:latest
- -f docker/Dockerfile . &&
- docker buildx rm cat-bulder
diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml
deleted file mode 100644
index 4644326e6e..0000000000
--- a/.github/workflows/maven-build.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
-# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
-
-name: Maven Build CI
-
-on:
- workflow_dispatch:
- inputs:
- logLevel:
- description: 'Log level'
- required: false
- default: 'warning'
- type: choice
- options:
- - info
- - warning
- - debug
- push:
- branches:
- - master
-jobs:
- build:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- - name: Set up JDK 8
- uses: actions/setup-java@v3
- with:
- java-version: '8'
- distribution: 'temurin'
- cache: 'maven'
-
- - name: Build with Maven
- run: mvn clean package -DskipTests
diff --git a/.github/workflows/maven-ci.yml b/.github/workflows/maven-ci.yml
new file mode 100644
index 0000000000..8daa11e537
--- /dev/null
+++ b/.github/workflows/maven-ci.yml
@@ -0,0 +1,58 @@
+# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
+# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
+
+name: Maven CI
+
+on:
+ push:
+ branches:
+ - main
+ - 3.*
+ paths-ignore:
+ - '**.md'
+ pull_request:
+ paths-ignore:
+ - '**.md'
+
+jobs:
+ build:
+ name: Build on java ${{ matrix.java }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ permissions:
+ contents: read
+ packages: write
+ strategy:
+ matrix:
+ java: ['8']
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Set up java ${{ matrix.java }}
+ uses: actions/setup-java@v3
+ with:
+ java-version: ${{ matrix.java }}
+ check-latest: true
+ distribution: 'temurin'
+ server-id: github
+ settings-path: ${{ github.workspace }}
+
+ - name: Build with Maven
+ run: |
+ chmod +x mvnw
+ ./mvnw -ntp package -DskipTests -U -T 4C -s $GITHUB_WORKSPACE/settings.xml
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+ - name: Publish to Docker Hub
+ run: |
+ docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
+ docker build -f docker/Dockerfile -t $DOCKER_IMAGE:$DOCKER_VERSION -t $DOCKER_IMAGE:latest .
+ docker push $DOCKER_IMAGE:$DOCKER_VERSION
+ docker push $DOCKER_IMAGE:latest
+ env:
+ DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
+ DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
+ DOCKER_IMAGE: shiyindaxiaojie/cat-home
+ DOCKER_VERSION: v3.4.1
diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml
deleted file mode 100644
index 4fe4df9144..0000000000
--- a/.github/workflows/maven-deploy.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
-# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
-
-name: Maven Deploy
-
-on:
- workflow_dispatch:
- inputs:
- auto_release:
- description: 'Run an automated release to the Central Repository'
- required: true
- type: boolean
- default: false
-jobs:
- maven_deploy:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- - name: Set up JDK 8
- uses: actions/setup-java@v3
- with:
- java-version: '8'
- distribution: 'temurin'
- cache: 'maven'
- server-id: ossrh
- server-username: MAVEN_USERNAME
- server-password: MAVEN_PASSWORD
- gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
- gpg-passphrase: MAVEN_GPG_PASSPHRASE
-
- - name: Publish to the Maven Central Repository
- if: ${{ github.event.inputs.auto_release == 'false'}}
- run: mvn deploy -B -DskipTests -pl :parent,:cat-client -P github-action
- env:
- MAVEN_USERNAME: ${{ secrets.MAVEN_USER }}
- MAVEN_PASSWORD: ${{ secrets.MAVEN_TOKEN }}
- MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
-
- - name: Publish and release to the Maven Central Repository
- if: ${{ github.event.inputs.auto_release == 'true'}}
- run: mvn deploy -B -DskipTests -pl :parent,:cat-client -Ddeploy.autoRelease=true -P github-action
- env:
- MAVEN_USERNAME: ${{ secrets.MAVEN_USER }}
- MAVEN_PASSWORD: ${{ secrets.MAVEN_TOKEN }}
- MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 89bf20bd0e..49de3bfde7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,34 +1,73 @@
-/ws/*
-/target/*
-/bin/*
-/*/target/*
-/*/bin/*
-/*.bat
-*.iml
-*.ipr
-*.iws
-/lib/java/target/*
-
-# Hidden files
-.*
-!.travis.yml
-!.gitignore
-!.gitattributes
-!.gitmodules
-
-# Node rules:
-## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-## Dependency directory
-## Commenting this out is preferred by some people, see
-## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
-node_modules
-
-# Book build output
-_book
-
-# eBook build output
-*.epub
-*.mobi
-*.pdf
+# Java
+*.class
+*.db
+*.ear
+*.jar
+*.war
+
+# Intellij
+.idea/
+classes/
+*.ids
+*.iml
+*.ipr
+*.iws
+*.orig
+
+# Eclipse
+.settings/
+.classpath
+.factorypath
+.loadpath
+.metadata
+.project
+*.bak
+*.launch
+*.pydevproject
+*.swp
+*.tmp
+*~.nib
+local.properties
+
+# Visual Studio Code
+.vscode/
+
+# JRebel
+rebel.xml
+
+# Maven
+!.mvn/wrapper/maven-wrapper.jar
+!maven/wrapper/maven-wrapper.jar
+maven/
+target/
+.flattened-pom.xml
+release.properties
+
+# Gradle
+!gradle/wrapper/maven-wrapper.jar
+.gradle/
+build/
+
+# SVN
+.svn/
+
+# Mac OSX
+.DS_Store
+
+# Windows
+._*
+*.*~
+*~
+.merge_file*
+Desktop.ini
+Thumbs.db
+
+# Others
+logs/
+log/
+.log
+.buildpath
+.cproject
+.externalToolBuilders/**
+.Spotlight-V100
+.Trashes
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000000..413100d221
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2007-present 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
+ *
+ * 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 java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo1.maven.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION
+ + "/maven-wrapper-"
+ + WRAPPER_VERSION
+ + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to use
+ * instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '"
+ + outputFile.getParentFile().getAbsolutePath()
+ + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(
+ new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000..2cc7d4a55c
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..c8c45495b6
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+#
+# Copyright 2012-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.
+#
+
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo1.maven.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/.sonarcloud.properties b/.sonarcloud.properties
new file mode 100644
index 0000000000..9638264635
--- /dev/null
+++ b/.sonarcloud.properties
@@ -0,0 +1,31 @@
+#
+# Copyright 2012-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.
+#
+
+# Path to sources
+#sonar.sources=
+#sonar.exclusions=resources/**/*,groovy/**/*,
+#sonar.inclusions=
+
+# Path to tests
+#sonar.tests=
+#sonar.test.exclusions=
+#sonar.test.inclusions=
+
+# Source encoding
+sonar.sourceEncoding=UTF-8
+
+# Exclusions for copy-paste detection
+#sonar.cpd.exclusions=
diff --git a/.travis.yml b/.travis.yml
index 9d4b1b0960..682f4031e0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,16 +11,16 @@ services:
before_install:
- export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
- mysql -e 'CREATE DATABASE IF NOT EXISTS cat;'
- - mysql -u root --password="" cat < script/CatApplication.sql
+ - mysql -u root --password="" cat < docs/script/cat-init-3.1.0.sql
- mkdir -p $HOME/data/appdatas/cat && mkdir -p $HOME/data/applogs
- - cp ./docker/datasources.xml $HOME/data/appdatas/cat && export MYSQL_URL="127.0.0.1" && export MYSQL_PORT="3306" && export MYSQL_USERNAME="root" && export MYSQL_PASSWD="" && export MYSQL_SCHEMA="cat" && sed "s# /data/# $HOME/data/#g" ./docker/datasources.sh > $HOME/data/datasources.sh && sh $HOME/data/datasources.sh
- - cp ./docker/client.xml $HOME/data/appdatas/cat/
+ - cp ./docs/datasources.xml $HOME/data/appdatas/cat && export MYSQL_URL="127.0.0.1" && export MYSQL_PORT="3306" && export MYSQL_USERNAME="root" && export MYSQL_PASSWD="" && export MYSQL_SCHEMA="cat" && sed "s# /data/# $HOME/data/#g" ./docker/datasources.sh > $HOME/data/datasources.sh && sh $HOME/data/datasources.sh
+ - cp ./docs/client.xml $HOME/data/appdatas/cat/
# - echo -e '\n\n /home/travis/.m2\n \n mvnsearch-unavailable\n mvnsearch-unavailable\n mvnsearch\n http://repo1.maven.org/maven2\n \n \n \n \n no-mvnsearch\n \n \n mvnsearch\n http://www.mvnsearch.org/maven2\n \n true\n \n \n true\n \n \n \n \n \n \n no-mvnsearch\n \n' > $HOME/.m2/settings.xml
-# - cat $HOME/.m2/settings.xml
+# - cat $HOME/.m2/settings.xml
- bash -c " ls -l ~/.m2 && du -sm ~/.m2/ "
install:
# bash -c "ls -l $HOME/data/appdatas/cat"
- export CAT_HOME=$HOME/data/appdatas/cat && mvn install -DskipTests -Dorg.slf4j.simpleLogger.defaultLogLevel=warn
+ export CAT_HOME=$HOME/data/appdatas/cat && mvn install -DskipTests -Dorg.slf4j.simpleLogger.defaultLogLevel=warn
script:
#bash -c " ls -l $HOME/data/appdatas/cat "
- bash -c " export CAT_HOME=$HOME/data/appdatas/cat && mvn test -Dorg.slf4j.simpleLogger.defaultLogLevel=warn"
\ No newline at end of file
+ bash -c " export CAT_HOME=$HOME/data/appdatas/cat && mvn test -Dorg.slf4j.simpleLogger.defaultLogLevel=warn"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000..5b2b9759e9
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,72 @@
+# Changelog
+
+## 3.4.1 (2024-08-27)
+
+### FEATURES
+
+- 新增 JVM 内置参数,支持 G1、CMS、ZGC 垃圾回收器
+
+### BUGFIX
+
+- 修复 `HeartBeat` 监控告警交互问题
+- 修复 Jira Software 兼容性问题
+- 修复飞书和钉钉告警配置问题
+
+## 3.4.0 (2024-05-01)
+
+### FEATURES
+
+- 支持告警自动录入 Jira Software
+
+### SCRIPTS
+
+- docs/scripts/cat-upgrade-3.4.0.sql
+
+## 3.3.2 (2024-01-26)
+
+### BUGFIX
+
+- 修复 Docker 环境下配置 CAT 集群的问题
+
+## 3.3.1 (2023-09-07)
+
+### BUGFIX
+
+- 修复 `Server`、`Browser`、`Mobile` 监控跳转交互问题
+
+## 3.3.0 (2023-06-30)
+
+### FEATURES
+
+- 新增 `Server`、`Browser`、`Mobile` 视图
+
+### SCRIPTS
+
+- docs/scripts/cat-upgrade-3.3.0.sql
+
+### IMPROVEMENTS
+
+- 优化消息树详情展示滚动条交互
+
+## 3.2.0 (2023-04-04)
+
+### FEATURES
+
+- 支持邮件、钉钉、企业微信、飞书机器人推送,无需部署额外资源
+- 新增应用大盘、数据库大盘、缓存大盘、服务大盘告警
+
+### IMPROVEMENTS
+
+- 优化 `Business`、`Exception`、`HeartBeat`、`Transaction`、`Event` 告警类型
+
+## 3.1.0 (2023-03-03)
+
+### FEATURES
+
+- 新增链路跟踪,支持 `traceId` 检索(建议客户端集成 [`eden-architect`](https://github.com/shiyindaxiaojie/eden-architect.git) 框架)
+- 新增服务 `Matrix` 性能报告
+
+### IMPROVEMENTS
+
+- 界面美化,遵循 Bootstrap 风格
+- 中文汉化,适合初学者入门
diff --git a/NOTICE.txt b/NOTICE.txt
index fc348a437d..07a808bcd3 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -11,7 +11,7 @@ Foundation (http://www.apache.org/),including, but not limit to:
- Apache Maven
- Apache log4j
-This product includes the several frameworks developed by
+This product includes the several frameworks developped by
unidal.org (https://github.com/unidal/),including:
- maven-plugins (https://github.com/unidal/maven-plugins)
- frameworks (https://github.com/unidal/frameworks)
diff --git a/README.md b/README.md
index 3f07c02570..2ec56bd165 100644
--- a/README.md
+++ b/README.md
@@ -1,115 +1,207 @@
-
+
-**CAT**
-==========
-[![GitHub stars](https://img.shields.io/github/stars/dianping/cat.svg?style=flat-square&label=Star&)](https://github.com/dianping/cat/stargazers)
-[![GitHub forks](https://img.shields.io/github/forks/dianping/cat.svg?style=flat-square&label=Fork&)](https://github.com/dianping/cat/fork)
-![Maven Central](https://img.shields.io/maven-central/v/org.unidal.framework/dal-jdbc)
-![License](https://img.shields.io/github/license/dianping/cat.svg)
-[![Build](https://github.com/dianping/cat/actions/workflows/build.yml/badge.svg)](https://github.com/dianping/cat/actions/workflows/build.yml)
+[license-apache2.0]:https://www.apache.org/licenses/LICENSE-2.0.html
-### CAT 简介
+[github-action]:https://github.com/shiyindaxiaojie/cat/actions
-- CAT 是基于 Java 开发的实时应用监控平台,为美团点评提供了全面的实时监控告警服务。
-- CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
-- CAT 很大的优势是它是一个实时系统,CAT 大部分系统是分钟级统计,但是从数据生成到服务端处理结束是秒级别,秒级定义是48分钟40秒,基本上看到48分钟38秒数据,整体报表的统计粒度是分钟级;第二个优势,监控数据是全量统计,客户端预计算;链路数据是采样计算。
+[sonarcloud-dashboard]:https://sonarcloud.io/dashboard?id=shiyindaxiaojie_cat
-### Cat 产品价值
+# CAT 实时监控平台
-- 减少故障发现时间
-- 降低故障定位成本
-- 辅助应用程序优化
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/readme/language-java-blue.svg) [![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/readme/license-apache2.0-red.svg)][license-apache2.0] [![](https://github.com/shiyindaxiaojie/cat/actions/workflows/maven-ci.yml/badge.svg?branch=3.1.x)][github-action]
-### Cat 优势
+CAT 是美团点评开源的实时应用监控平台,提供了 `Tracsaction`、`Event`、`Problem`、`Business` 等丰富的指标项。在实际的生产需求中,笔者进行了部分扩展:
+1. 链路跟踪:通过日志的链路ID 搜索消息树,定位问题更高效。
+2. 告警优化:支持邮件、钉钉、微信、飞书机器人推送,无需部署额外资源。
+3. 组件扩展:新增应用大盘、数据库大盘、缓存大盘、服务大盘告警。
+4. 工单集成:支持告警自动录入 Jira Software,提高问题处理效率。
-- 实时处理:信息的价值会随时间锐减,尤其是事故处理过程中
-- 全量数据:全量采集指标数据,便于深度分析故障案例
-- 高可用:故障的还原与问题定位,需要高可用监控来支撑
-- 故障容忍:故障不影响业务正常运转、对业务透明
-- 高吞吐:海量监控数据的收集,需要高吞吐能力做保证
-- 可扩展:支持分布式、跨 IDC 部署,横向扩展的监控系统
+本项目已投入生产使用,如果您有使用上的问题,欢迎查阅我的[笔记](https://mengxiangge.netlify.app/2024/05/01/%E6%96%B0%E6%89%8B%E5%BF%85%E7%9C%8B%EF%BC%8110%E5%88%86%E9%92%9F%E5%B8%A6%E4%BD%A0%E8%A7%A3%E5%86%B3%20cat%20%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98%E3%80%82/) 。
-> 由于仓库的git历史记录众多,对于不关注历史,只关注最新版本或者基于最新版本贡献的新用户,可以在第一次克隆代码时增加--depth=1参数以加快下载速度,如
-```bash
-git clone --depth=1 https://github.com/dianping/cat.git
+## 演示图例
+
+### 风格美化
+
+改造前:
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/dashboard-old.png)
+
+改造后:
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/dashboard.png)
+
+### 链路跟踪(New)
+
+通过日志打印的 TraceId 查找整个请求路径的 HTTP 请求耗时、RPC 调用情况、Log4j2 业务日志、SQL 和缓存执行耗时。
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/tracing.png)
+
+### 告警监控(New)
+
+支持邮件、钉钉、微信、飞书机器人推送,无需部署额外资源。
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/dingtalk.png)
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/mail.png)
+
+### 大盘优化(New)
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/app-dashboard.png)
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/database-dashboard.png)
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/cache-dashboard.png)
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/rpc-dashboard.png)
+
+### 其他优化
+
+#### Transaction
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/transaction.png)
+
+#### Event
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/event.png)
+
+#### Business
+
+相对于 Transaction 和 Event 更宏观的指标,需要业务自己埋点。
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/business.png)
+
+推荐使用 [`eden-cat-spring-boot-starter`](https://github.com/shiyindaxiaojie/eden-architect/tree/main/eden-components/eden-spring-integration/src/main/java/org/ylzl/eden/spring/integration/cat) 提供的 `@CatMetric` 注解实现埋点,支持 SpEL 表达式,代码示例如下:
+
+```java
+@CatMetric(name = "'客户[' + #cust.custId + ']资产查询调用次数'", count = 1)
+public Response listAsset(Cust cust) {
+ //
+}
```
-### 更新日志
+#### Matrix
+
+统计所有接口的性能情况
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/matrix.png)
+
+#### Cross
+
+可以搜索某个 RPC 接口被调用的情况
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/rpc.png)
+
+#### Heart Beat
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/heartbeat.png)
+
+#### Dependency
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/dependency.png)
+
+#### Browser
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/browser.png)
+
+#### Mobile
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/mobile.png)
+
+#### State
+
+查看当前 CAT 和应用节点的状态
+
+![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/state.png)
+
+## 如何构建
+
+本项目默认使用 Maven 来构建,最快的使用方式是 `git clone` 到本地。在项目的根目录执行 `mvn install -T 4C` 完成本项目的构建。
-- [**最新版本特性一览**](https://github.com/dianping/cat/wiki/new)
+## 如何启动
- - 注意cat的3.0代码分支更新都发布在master上,包括最新文档也都是这个分支
- - 注意文档请用最新master里面的代码文档作为标准,一些开源网站上面一些老版本的一些配置包括数据库等可能遇到不兼容情况,请以master代码为准,这份文档都是美团点评内部同学为这个版本统一整理汇总。内部同学已经核对,包括也验证过,如果遇到一些看不懂,或者模糊的地方,欢迎提交PR。
- - 多语言客户端:Java、C/C++、Node.js、Python、Go [传送门](https://github.com/dianping/cat/tree/master/lib)
-
- * [**Java**](https://github.com/dianping/cat/blob/master/lib/java)
- * [**C**](https://github.com/dianping/cat/blob/master/lib/c)
- * [**C++**](https://github.com/dianping/cat/blob/master/lib/cpp)
- * [**Python**](https://github.com/dianping/cat/blob/master/lib/python)
- * [**Go**](https://github.com/dianping/cat/blob/master/lib/go)
- * [**Node.js**](https://github.com/dianping/cat/blob/master/lib/node.js)
-
- - 消息采样聚合
- - 序列化协议升级
- - 全新文件存储引擎
-
+### IDEA 启动
-### 监控模型:
+1. 在用户目录创建文件夹 `~/.cat/appdatas/cat`,拷贝本项目的 `docs/config` 到该目录下
+2. 修改 `docs/config/datasources.xml` 的数据库连接信息
+3. 在上述目标数据源执行 `scripts/cat-init-3.3.0.sql` 初始化
+4. 检查 `cat-home` 模块已正确设置了 Facet
+ ![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/idea-cat-home-facet.png)
+5. 使用 IDEA 配置 Tomcat 服务器,请注意,多网卡情况下可能会出现 `CAT服务端异常:[127.0.0.1]`,请设置 JVM 启动参数 `host.ip` 指定 IP。
+ ![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/idea-tomcat-settings.png)
+6. 指定访问入口 Context 为 `/cat`
+ ![](https://cdn.jsdelivr.net/gh/shiyindaxiaojie/images/cat/idea-tomcat-deployment.png)
+7. 运行 Tomcat 服务器,启动成功后,自动打开 `http://localhost:8080/cat`
-支持 Transaction、Event、Heartbeat、Metric 四种消息模型。 [**模型设计**](https://github.com/dianping/cat/wiki/model)
+### Docker 启动
-### 模块简介
+本项目已发布到 [Docker Hub](https://hub.docker.com/repository/docker/shiyindaxiaojie/cat-home),请参考以下命令运行。
-#### 功能模块
+ ```bash
+ docker run -e MYSQL_URL="127.0.0.1" -e MYSQL_PORT="3306" -e MYSQL_SCHEMA="cat" -e MYSQL_USERNAME="" -e MYSQL_PASSWD="" -p 8080:8080 --name=cat-home -d shiyindaxiaojie/cat-home
+ ```
-- cat-client: 客户端,上报监控数据
-- cat-consumer: 服务端,收集监控数据进行统计分析,构建丰富的统计报表
-- cat-alarm: 实时告警,提供报表指标的监控告警
-- cat-hadoop: 数据存储,logview 存储至 Hdfs
-- cat-home: 管理端,报表展示、配置管理等
+## 如何部署
-> 1. 根目录下 cat-client 模块以后不再维护,下个大版本更新计划移除。新版Java客户端参考:lib/java
-> 2. 管理端、服务端、告警服务均使用 cat-home 模块部署即可
+> 注意:除了 Helm 部署,在停止 CAT 进程之前最好调用 `curl http://localhost:8080/cat/r/home?op=checkpoint` ,将内存数据持久化磁盘,避免重启后丢失。
-#### 其他模块
+### Tomcat 部署
-- integration:cat和一些第三方工具集成的内容(此部分一部分是由社区贡献,一部分官方贡献)
-- lib:CAT 的客户端,包括 Java、C/C++、Python、Node.js、Go
-- script:CAT 数据库脚本
+拷贝本项目的 `docs/config` 到用户目录 `~/.cat/appdatas/cat` 中,按需调整数据库配置。执行 `mvn clean package` 打包成一个 cat-home.war,部署在目标 Tomcat 的 `webapps` 目录下,启动 Tomcat 即可。
-### Quick Start
+### Docker 部署
-- [部署FAQ](https://github.com/dianping/cat/wiki/cat_faq)
+在项目根目录执行 `docker build -f docker/Dockerfile cat:{tag} .` 打包为镜像。
-#### 服务端
+### Helm 部署
-- [集群部署](https://github.com/dianping/cat/wiki/readme_server)
-- [报表介绍](https://github.com/dianping/cat/wiki/readme_report)
-- [配置手册](https://github.com/dianping/cat/wiki/readme_config)
+进入 `helm` 目录,执行 `helm install -n cat cat .` 安装,在 K8s 环境将自动创建 CAT 所需的资源文件。
-### 项目设计
+## 如何接入
-- [项目架构](https://github.com/dianping/cat/wiki/overall)
-- [客户端设计](https://github.com/dianping/cat/wiki/client)
-- [服务端设计](https://github.com/dianping/cat/wiki/server)
-- [模型设计](https://github.com/dianping/cat/wiki/model)
+为了减少客户端集成的工作,您可以使用 [eden-architect](https://github.com/shiyindaxiaojie/eden-architect) 框架,只需要两步就可以完成 CAT 的集成。
-### Copyright and License
+1. 引入 CAT 依赖
+````xml
+
+ io.github.shiyindaxiaojie
+ eden-cat-spring-boot-starter
+
+````
+2. 开启 CAT 配置
+````yaml
+cat:
+ enabled: false # 默认关闭,请按需开启
+ trace-mode: true # 开启访问观测
+ support-out-trace-id: false # 允许异构子系统间透传链路ID
+ home: /tmp
+ servers: localhost # CAT 地址
+ tcp-port: 2280
+ http-port: 8080
-[Apache 2.0 License.](/LICENSE)
+# 如果您使用 Dubbo 组件,请增加对应的过滤器,确保 CAT 埋点正常工作
+dubbo:
+ provider:
+ filter: cat-tracing
+ consumer:
+ filter: cat-tracing,cat-consumer
+````
-### CAT 接入公司
+另外,笔者提供了两种不同应用架构的示例,里面有集成 CAT 的示例。
+* 面向领域模型的 **COLA 架构**,代码实例可以查看 [eden-demo-cola](https://github.com/shiyindaxiaojie/eden-demo-cola)
+* 面向数据模型的 **分层架构**,代码实例请查看 [eden-demo-layer](https://github.com/shiyindaxiaojie/eden-demo-layer)
-![Alt text](cat-home/src/main/webapp/images/logo/companys.png)
+## 版本规范
-更多接入公司,欢迎在 登记
+项目的版本号格式为 `x.y.z` 的形式,其中 x 的数值类型为数字,从 0 开始取值,且不限于 0~9 这个范围。项目处于孵化器阶段时,第一位版本号固定使用 0,即版本号为 `0.x.x` 的格式。
-### 联系我们
+* 孵化版本:0.0.1-SNAPSHOT
+* 开发版本:1.0.0-SNAPSHOT
+* 发布版本:1.0.0
-我们需要知道你对Cat的一些看法以及建议:
+版本迭代规则:
-- [**Issues**](https://github.com/dianping/cat/issues)
+* 1.0.0 <> 1.0.1:兼容
+* 1.0.0 <> 1.1.0:基本兼容
+* 1.0.0 <> 2.0.0:不兼容
-### Starred 趋势
+## 变更日志
-[![Star History Chart](https://api.star-history.com/svg?repos=dianping/cat&type=Date)](https://star-history.com/#dianping/cat&Date)
+请查阅 [CHANGELOG.md](https://github.com/shiyindaxiaojie/cat/blob/main/CHANGELOG.md)
diff --git a/cat-alarm/pom.xml b/cat-alarm/pom.xml
index 0d7c8058ce..cb57da26aa 100644
--- a/cat-alarm/pom.xml
+++ b/cat-alarm/pom.xml
@@ -1,139 +1,170 @@
-
-
- com.dianping.cat
- parent
- 4.0-RC1
-
- 4.0.0
- cat-alarm
- cat-alarm
- jar
-
-
- com.dianping.cat
- cat-client
-
-
- com.dianping.cat
- cat-core
-
-
- org.unidal.framework
- foundation-service
-
-
- org.unidal.framework
- web-framework
-
-
- org.unidal.framework
- dal-jdbc
-
-
- javax.servlet
- servlet-api
- provided
-
-
- log4j
- log4j
- true
- provided
-
-
- org.freemarker
- freemarker
-
-
- org.unidal.framework
- test-framework
- test
-
-
- commons-lang
- commons-lang
-
-
- com.google.code.gson
- gson
-
-
- commons-codec
- commons-codec
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.httpcomponents
- httpmime
-
-
- com.alibaba
- fastjson
-
-
- junit
- junit
- test
-
-
-
-
-
- org.unidal.maven.plugins
- codegen-maven-plugin
-
-
- generate data model
- generate-sources
-
- dal-model
-
-
- ${basedir}/src/main/resources/META-INF/dal/model/server-alarm-rule-manifest.xml,
- ${basedir}/src/main/resources/META-INF/dal/model/sender-config-manifest.xml,
- ${basedir}/src/main/resources/META-INF/dal/model/alert-policy-manifest.xml,
- ${basedir}/src/main/resources/META-INF/dal/model/monitor-rules-manifest.xml,
- ${basedir}/src/main/resources/META-INF/dal/model/alert-receiver-manifest.xml,
-
-
-
-
- generate dal jdbc model
- generate-sources
-
- dal-jdbc
-
-
- ${basedir}/src/main/resources/META-INF/dal/jdbc/alarm-manifest.xml,
-
-
-
-
-
- org.unidal.maven.plugins
- plexus-maven-plugin
-
-
- generate plexus component descriptor
- process-classes
-
- plexus
-
-
- com.dianping.cat.build.ComponentsConfigurator
-
-
-
-
-
-
-
- utf-8
-
+
+
+ com.dianping.cat
+ cat-parent
+ 3.4.2-SNAPSHOT
+
+ 4.0.0
+ cat-alarm
+ cat-alarm
+ jar
+
+
+ com.dianping.cat
+ cat-client
+
+
+ com.dianping.cat
+ cat-core
+
+
+ org.unidal.framework
+ foundation-service
+
+
+ plexus-utils
+ org.codehaus.plexus
+
+
+ guava
+ com.google.guava
+
+
+
+
+ org.unidal.framework
+ web-framework
+
+
+ org.unidal.framework
+ dal-jdbc
+
+
+ javax.servlet
+ servlet-api
+ provided
+
+
+ log4j
+ log4j
+ true
+ provided
+
+
+ org.freemarker
+ freemarker
+
+
+ org.unidal.framework
+ test-framework
+ test
+
+
+ commons-lang
+ commons-lang
+
+
+ com.google.code.gson
+ gson
+
+
+ commons-codec
+ commons-codec
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ httpcore
+ org.apache.httpcomponents
+
+
+
+
+ org.apache.httpcomponents
+ httpmime
+
+
+ com.alibaba
+ fastjson
+
+
+ junit
+ junit
+ test
+
+
+ com.sun.mail
+ jakarta.mail
+ 1.6.7
+
+
+ com.atlassian.jira
+ jira-rest-java-client-core
+ 5.2.7
+
+
+ io.atlassian.fugue
+ fugue
+ 4.7.2
+
+
+
+
+
+ org.unidal.maven.plugins
+ codegen-maven-plugin
+
+
+ generate data model
+ generate-sources
+
+ dal-model
+
+
+ ${basedir}/src/main/resources/META-INF/dal/model/server-alarm-rule-manifest.xml,
+ ${basedir}/src/main/resources/META-INF/dal/model/sender-config-manifest.xml,
+ ${basedir}/src/main/resources/META-INF/dal/model/alert-policy-manifest.xml,
+ ${basedir}/src/main/resources/META-INF/dal/model/monitor-rules-manifest.xml,
+ ${basedir}/src/main/resources/META-INF/dal/model/alert-receiver-manifest.xml,
+ ${basedir}/src/main/resources/META-INF/dal/model/crash-alarm-rule-manifest.xml,
+
+
+
+
+ generate dal jdbc model
+ generate-sources
+
+ dal-jdbc
+
+
+ ${basedir}/src/main/resources/META-INF/dal/jdbc/alarm-manifest.xml,
+
+
+
+
+
+ org.unidal.maven.plugins
+ plexus-maven-plugin
+
+
+ generate plexus component descriptor
+ process-classes
+
+ plexus
+
+
+ com.dianping.cat.build.ComponentsConfigurator
+
+
+
+
+
+
+
+ utf-8
+
diff --git a/cat-alarm/src/main/java/com/dianping/cat/alarm/AlertMonitor.java b/cat-alarm/src/main/java/com/dianping/cat/alarm/AlertMonitor.java
new file mode 100644
index 0000000000..1e2b257cdb
--- /dev/null
+++ b/cat-alarm/src/main/java/com/dianping/cat/alarm/AlertMonitor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-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 com.dianping.cat.alarm;
+
+/**
+ * TODO
+ *
+ * @author gyl
+ * @since 2.4.x
+ */
+public enum AlertMonitor {
+
+ COUNT("执行次数"),
+ AVG("响应时间"),
+ FAILRATIO("失败率"),
+ MAX("最大响应时间");
+
+ private String text;
+
+ AlertMonitor(String text) {
+ this.text = text;
+ }
+
+ public static String parseText(String name) {
+ for (AlertMonitor monitor : AlertMonitor.values()) {
+ if (monitor.name().equalsIgnoreCase(name)) {
+ return monitor.text;
+ }
+ }
+ return name;
+ }
+}
diff --git a/cat-alarm/src/main/java/com/dianping/cat/alarm/app/AppAlarmRuleParam.java b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/AppAlarmRuleParam.java
new file mode 100644
index 0000000000..9b9e9d45dd
--- /dev/null
+++ b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/AppAlarmRuleParam.java
@@ -0,0 +1,151 @@
+package com.dianping.cat.alarm.app;
+
+import com.dianping.cat.app.AppDataField;
+
+public class AppAlarmRuleParam implements Cloneable {
+
+ private int m_command;
+
+ private String m_commandName;
+
+ private int m_code;
+
+ private int m_network;
+
+ private int m_version;
+
+ private int m_connectType;
+
+ private int m_platform;
+
+ private int m_city;
+
+ private int m_operator;
+
+ private String m_metric;
+
+ private AppDataField m_groupBy;
+
+ public int getCity() {
+ return m_city;
+ }
+
+ public int getCode() {
+ return m_code;
+ }
+
+ public int getCommand() {
+ return m_command;
+ }
+
+ public String getCommandName() {
+ return m_commandName;
+ }
+
+ public int getConnectType() {
+ return m_connectType;
+ }
+
+ public AppDataField getGroupBy() {
+ return m_groupBy;
+ }
+
+ public String getMetric() {
+ return m_metric;
+ }
+
+ public int getNetwork() {
+ return m_network;
+ }
+
+ public int getOperator() {
+ return m_operator;
+ }
+
+ public int getPlatform() {
+ return m_platform;
+ }
+
+ public int getVersion() {
+ return m_version;
+ }
+
+ public boolean isEachAlarm() {
+ return m_groupBy != null;
+ }
+
+ public boolean getEachAlarm() {
+ return m_groupBy != null;
+ }
+
+ public void setCity(int city) {
+ m_city = city;
+ }
+
+ public void setCode(int code) {
+ m_code = code;
+ }
+
+ public void setCommand(int command) {
+ m_command = command;
+ }
+
+ public void setCommandName(String commandName) {
+ m_commandName = commandName;
+ }
+
+ public void setConnectType(int connectType) {
+ m_connectType = connectType;
+ }
+
+ public void setGroupBy(AppDataField groupBy) {
+ m_groupBy = groupBy;
+ }
+
+ public void setMetric(String metric) {
+ m_metric = metric;
+ }
+
+ public void setNetwork(int network) {
+ m_network = network;
+ }
+
+ public void setOperator(int operator) {
+ m_operator = operator;
+ }
+
+ public void setPlatform(int platform) {
+ m_platform = platform;
+ }
+
+ public void setVersion(int version) {
+ m_version = version;
+ }
+
+ @Override
+ public AppAlarmRuleParam clone() throws CloneNotSupportedException {
+ AppAlarmRuleParam param = new AppAlarmRuleParam();
+
+ param.setCommand(m_command);
+ param.setCommandName(m_commandName);
+ param.setCode(m_code);
+ param.setConnectType(m_connectType);
+ param.setMetric(m_metric);
+ param.setGroupBy(m_groupBy);
+ param.setNetwork(m_network);
+ param.setCity(m_city);
+ param.setOperator(m_operator);
+ param.setPlatform(m_platform);
+ param.setVersion(m_version);
+ return param;
+ }
+
+ @Override
+ public String toString() {
+ return "AppAlarmRuleParam [m_command=" + m_command + ", m_commandName=" + m_commandName + ", m_code=" + m_code
+ + ", m_network=" + m_network + ", m_version=" + m_version + ", m_connectType=" + m_connectType
+ + ", m_platform=" + m_platform + ", m_city=" + m_city + ", m_operator=" + m_operator + ", m_metric="
+ + m_metric + ", m_groupBy=" + m_groupBy + "]";
+ }
+
+}
diff --git a/cat-alarm/src/main/java/com/dianping/cat/alarm/app/AppAlarmRuleParamBuilder.java b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/AppAlarmRuleParamBuilder.java
new file mode 100644
index 0000000000..3bc5dbe766
--- /dev/null
+++ b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/AppAlarmRuleParamBuilder.java
@@ -0,0 +1,133 @@
+package com.dianping.cat.alarm.app;
+
+import com.dianping.cat.Cat;
+import com.dianping.cat.alarm.rule.entity.Rule;
+import com.dianping.cat.app.AppDataField;
+import com.dianping.cat.config.app.MobileConfigManager;
+import com.dianping.cat.config.app.MobileConstants;
+import org.unidal.lookup.annotation.Inject;
+import org.unidal.lookup.annotation.Named;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+@Named
+public class AppAlarmRuleParamBuilder {
+
+ @Inject
+ private MobileConfigManager m_mobileConfigManager;
+
+ public static final String COMMAND = "command";
+
+ public static final String COMMAND_NAME = "commandName";
+
+ public static final String CODE = "code";
+
+ public static final String NETWORK = MobileConstants.NETWORK;
+
+ public static final String VERSION = MobileConstants.VERSION;
+
+ public static final String CONNECT_TYPE = MobileConstants.CONNECT_TYPE;
+
+ public static final String PLATFORM = MobileConstants.PLATFORM;
+
+ public static final String CITY = MobileConstants.CITY;
+
+ public static final String OPERATOR = MobileConstants.OPERATOR;
+
+ public static final String METRIC = "metric";
+
+ public List build(Rule rule) {
+ List results = new ArrayList();
+ Map attributes = new LinkedHashMap();
+ List starKeys = new ArrayList();
+
+ for (Entry entry : rule.getDynamicAttributes().entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+
+ if ("*".equals(value)) {
+ value = "-1";
+ starKeys.add(key);
+ }
+ attributes.put(key, value);
+ }
+
+ if (starKeys.size() > 0) {
+ for (String key : starKeys) {
+ try {
+ AppAlarmRuleParam param = buildParam(attributes);
+ AppDataField dataField = AppDataField.getByTitle(key);
+
+ param.setGroupBy(dataField);
+ results.add(param);
+ } catch (Exception e) {
+ Cat.logError(rule.toString(), e);
+ }
+ }
+ } else {
+ results.add(buildParam(attributes));
+ }
+ return results;
+ }
+
+ private AppAlarmRuleParam buildParam(Map attrs) throws NumberFormatException {
+ int command = Integer.parseInt(attrs.get(COMMAND));
+ String commandName = attrs.get(COMMAND_NAME);
+ int code = Integer.parseInt(attrs.get(CODE));
+ int network = Integer.parseInt(attrs.get(NETWORK));
+ int version = Integer.parseInt(attrs.get(VERSION));
+ int connectType = Integer.parseInt(attrs.get(CONNECT_TYPE));
+ int platform = Integer.parseInt(attrs.get(PLATFORM));
+ int city = Integer.parseInt(attrs.get(CITY));
+ int operator = Integer.parseInt(attrs.get(OPERATOR));
+ String metric = attrs.get(METRIC);
+
+ AppAlarmRuleParam param = new AppAlarmRuleParam();
+
+ param.setCommand(command);
+ param.setCommandName(commandName);
+ param.setCode(code);
+ param.setNetwork(network);
+ param.setVersion(version);
+ param.setConnectType(connectType);
+ param.setPlatform(platform);
+ param.setCity(city);
+ param.setOperator(operator);
+ param.setMetric(metric);
+ return param;
+ }
+
+ public void setField(AppAlarmRuleParam param, int value) {
+ switch (param.getGroupBy()) {
+ case OPERATOR:
+ param.setOperator(value);
+ break;
+ case APP_VERSION:
+ param.setVersion(value);
+ break;
+ case CITY:
+ param.setCity(value);
+ break;
+ case CONNECT_TYPE:
+ param.setConnectType(value);
+ break;
+ case NETWORK:
+ param.setNetwork(value);
+ break;
+ case PLATFORM:
+ param.setPlatform(value);
+ break;
+ case CODE:
+ case SOURCE:
+ break;
+ }
+ }
+
+ public void setMobileConfigManager(MobileConfigManager manager) {
+ m_mobileConfigManager = manager;
+ }
+}
diff --git a/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashAlert.java b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashAlert.java
new file mode 100644
index 0000000000..a60915e723
--- /dev/null
+++ b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashAlert.java
@@ -0,0 +1,132 @@
+package com.dianping.cat.alarm.app.crash;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.unidal.helper.Threads.Task;
+import org.unidal.lookup.annotation.Inject;
+import org.unidal.lookup.annotation.Named;
+
+import com.dianping.cat.Cat;
+import com.dianping.cat.alarm.crash.entity.ExceptionLimit;
+import com.dianping.cat.alarm.spi.AlertEntity;
+import com.dianping.cat.alarm.spi.AlertLevel;
+import com.dianping.cat.alarm.spi.AlertManager;
+import com.dianping.cat.alarm.spi.AlertType;
+import com.dianping.cat.app.crash.CrashLog;
+import com.dianping.cat.app.crash.CrashLogDao;
+import com.dianping.cat.app.crash.CrashLogEntity;
+import com.dianping.cat.config.app.CrashLogConfigManager;
+import com.dianping.cat.config.app.MobileConfigManager;
+import com.dianping.cat.helper.TimeHelper;
+import com.dianping.cat.message.Transaction;
+
+@Named
+public class CrashAlert implements Task {
+
+ protected static final long DURATION = TimeHelper.ONE_MINUTE;
+
+ @Inject
+ private CrashLogDao m_crashLogDao;
+
+ @Inject
+ private CrashRuleConfigManager m_crashRuleConfigManager;
+
+ @Inject
+ private CrashLogConfigManager m_crashLogConfigManager;
+
+ @Inject
+ private MobileConfigManager m_mobileConfigManager;
+
+ @Inject
+ protected AlertManager m_sendManager;
+
+ @Override
+ public void run() {
+ boolean active = TimeHelper.sleepToNextMinute();
+
+ while (active) {
+ long current = System.currentTimeMillis();
+ Transaction t = Cat.newTransaction("AlertCrash", TimeHelper.getMinuteStr());
+
+ try {
+ Date startTime = new Date(current - TimeHelper.ONE_MINUTE * 2);
+ Date endTime = new Date(current - TimeHelper.ONE_MINUTE);
+ List limits = m_crashRuleConfigManager.queryAllExceptionLimits();
+
+ for (ExceptionLimit limit : limits) {
+ int appId = limit.getAppId();
+ String platformStr = limit.getPlatform();
+ String module = limit.getModule();
+ int platform = m_mobileConfigManager.getPlatformId(platformStr);
+
+ CrashLog result = m_crashLogDao.findCountByConditions(startTime, endTime, String.valueOf(appId),
+ platform, module, CrashLogEntity.READSET_COUNT_DATA);
+ int count = result.getCount();
+
+ if (count >= limit.getWarnings()) {
+ AlertEntity entity = new AlertEntity();
+ String appName = m_mobileConfigManager.getAppName(appId);
+
+ entity.setDate(startTime).setContent(buildContent(appName, module, count));
+ entity.setMetric(limit.getId()).setType(getName()).setGroup(module).setDomain(appName);
+ entity.setContactGroup(limit.getId());
+
+ if (count >= limit.getErrors()) {
+ entity.setLevel(AlertLevel.ERROR);
+ } else {
+ entity.setLevel(AlertLevel.WARNING);
+ }
+
+ Map paras = new HashMap();
+
+ paras.put("end", endTime);
+ paras.put("warning", limit.getWarnings());
+ paras.put("error", limit.getErrors());
+ paras.put("count", count);
+ paras.put("appId", appId);
+ paras.put("platform", platform);
+ entity.setParas(paras);
+
+ m_sendManager.addAlert(entity);
+ }
+ }
+
+ t.setStatus(Transaction.SUCCESS);
+ } catch (Exception e) {
+ t.setStatus(e);
+ } finally {
+ t.complete();
+ }
+
+ long duration = System.currentTimeMillis() - current;
+
+ try {
+ if (duration < DURATION) {
+ Thread.sleep(DURATION - duration);
+ }
+ } catch (InterruptedException e) {
+ active = false;
+ }
+ }
+ }
+
+ private String buildContent(String appName, String module, long count) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("[AppName: ").append(appName).append(" 模块: ").append(module).append(" 数量: ").append(count).append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public String getName() {
+ return AlertType.CRASH.getName();
+ }
+
+ @Override
+ public void shutdown() {
+ }
+
+}
diff --git a/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashContactor.java b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashContactor.java
new file mode 100644
index 0000000000..d61b508658
--- /dev/null
+++ b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashContactor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012-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 com.dianping.cat.alarm.app.crash;
+
+import com.dianping.cat.alarm.spi.AlertType;
+import com.dianping.cat.alarm.spi.receiver.Contactor;
+import com.dianping.cat.alarm.spi.receiver.ProjectContactor;
+import org.unidal.lookup.annotation.Inject;
+
+public class CrashContactor extends ProjectContactor implements Contactor {
+ public static final String ID = AlertType.CRASH.getName();
+
+ @Inject
+ protected CrashRuleConfigManager m_crashAlarmRuleManager;
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+}
diff --git a/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashDecorator.java b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashDecorator.java
new file mode 100644
index 0000000000..c059f6d27c
--- /dev/null
+++ b/cat-alarm/src/main/java/com/dianping/cat/alarm/app/crash/CrashDecorator.java
@@ -0,0 +1,88 @@
+package com.dianping.cat.alarm.app.crash;
+
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
+
+import com.dianping.cat.Cat;
+import com.dianping.cat.alarm.spi.AlertEntity;
+import com.dianping.cat.alarm.spi.AlertType;
+import com.dianping.cat.alarm.spi.decorator.Decorator;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+public class CrashDecorator extends Decorator implements Initializable {
+
+ public static final String ID = AlertType.CRASH.getName();
+
+ public Configuration m_configuration;
+
+ protected DateFormat m_dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+ protected DateFormat m_timeFormat = new SimpleDateFormat("HH:mm");
+
+ @Override
+ public void initialize() throws InitializationException {
+ m_configuration = new Configuration();
+ m_configuration.setDefaultEncoding("UTF-8");
+ try {
+ m_configuration.setClassForTemplateLoading(this.getClass(), "/freemaker");
+ } catch (Exception e) {
+ Cat.logError(e);
+ }
+ }
+
+ @Override
+ public String generateContent(AlertEntity alert) {
+ Map