From d47b04d383eb42962fc6ab4aaf5349d93470c978 Mon Sep 17 00:00:00 2001 From: Jason Hanna Date: Tue, 13 Sep 2022 13:25:46 +0000 Subject: [PATCH 1/4] CR-510: adding a new sentry logger for the candidate selector in the search based ref matcher --- .gitlab-ci.yml | 13 +++ pom.xml | 6 + sentry.properties | 1 + .../refmatching/CandidateSelector.java | 3 +- .../refmatching/ReferenceMatcher.java | 3 + .../crossref/refmatching/SentryLogger.java | 103 ++++++++++++++++++ .../java/SentryLogger/SentryLoggerTest.java | 49 +++++++++ 7 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 sentry.properties create mode 100644 src/main/java/org/crossref/refmatching/SentryLogger.java create mode 100644 src/test/java/SentryLogger/SentryLoggerTest.java diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f0069fe..05ae862 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,6 +41,19 @@ release: - VERSION.txt - CHANGELOG.md +release: + image: registry.gitlab.com/crossref/infrastructure/aws-ecr-ecs-cicd-docker:latest + stage: release + script: + - npx semantic-release + only: + - develop + - main + artifacts: + paths: + - VERSION.txt + - CHANGELOG.md + deploy: stage: deploy image: maven:3.3.9-jdk-8 diff --git a/pom.xml b/pom.xml index 1c5a413..80bc0aa 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,11 @@ 1.10.19 test + + io.sentry + sentry + 1.7.27 + reference-matching @@ -56,6 +61,7 @@ + org.apache.maven.plugins diff --git a/sentry.properties b/sentry.properties new file mode 100644 index 0000000..f45dc1c --- /dev/null +++ b/sentry.properties @@ -0,0 +1 @@ +dsn=http://17a5b3ec5566440c8ed1945a710c10a6@sentry.io/1769602 \ No newline at end of file diff --git a/src/main/java/org/crossref/refmatching/CandidateSelector.java b/src/main/java/org/crossref/refmatching/CandidateSelector.java index ead216a..10a3462 100644 --- a/src/main/java/org/crossref/refmatching/CandidateSelector.java +++ b/src/main/java/org/crossref/refmatching/CandidateSelector.java @@ -25,6 +25,7 @@ public class CandidateSelector { private final ICrossRefApiClient apiClient; private final Logger log = LogUtils.getLogger(); + private final SentryLogger sentryLogger = SentryLogger.getLoggerClass(CandidateSelector.class); public CandidateSelector(ICrossRefApiClient apiClient) { this.apiClient = apiClient; @@ -75,7 +76,7 @@ public class CandidateSelector { return candidates; } catch (IOException ex) { - log.error("Error calling api client: " + ex.getMessage(), ex); + sentryLogger.error("Error calling api client: " + ex.getMessage(), ex); return new ArrayList<>(); } } diff --git a/src/main/java/org/crossref/refmatching/ReferenceMatcher.java b/src/main/java/org/crossref/refmatching/ReferenceMatcher.java index ae7e4a1..a9bfa26 100644 --- a/src/main/java/org/crossref/refmatching/ReferenceMatcher.java +++ b/src/main/java/org/crossref/refmatching/ReferenceMatcher.java @@ -1,5 +1,6 @@ package org.crossref.refmatching; +import io.sentry.Sentry; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -30,6 +31,7 @@ public class ReferenceMatcher { private final CandidateSelector selector; private final CandidateValidator validator = new CandidateValidator(); private static final Logger LOGGER = LogUtils.getLogger(); + private static final String DSN = "http://17a5b3ec5566440c8ed1945a710c10a6@sentry.io/1769602"; /** * Constructor sets apiClient. @@ -63,6 +65,7 @@ public class ReferenceMatcher { * this just caches the journals depending on a flag. */ public void initialize() { + Sentry.init(); if (cacheJournalAbbrevMap) { try { InputStream is = ReferenceMatcher.class.getResourceAsStream( diff --git a/src/main/java/org/crossref/refmatching/SentryLogger.java b/src/main/java/org/crossref/refmatching/SentryLogger.java new file mode 100644 index 0000000..471d72e --- /dev/null +++ b/src/main/java/org/crossref/refmatching/SentryLogger.java @@ -0,0 +1,103 @@ +package org.crossref.refmatching; + +import io.sentry.Sentry; +import io.sentry.event.Event; +import io.sentry.event.EventBuilder; +import io.sentry.event.Sdk; +import io.sentry.event.interfaces.ExceptionInterface; +import java.util.Date; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import org.apache.log4j.*; + +public class SentryLogger extends Logger { + + private final org.apache.log4j.Logger logger; + + // Flag to turn event capturing on/off + private boolean captureEvents = true; + + public SentryLogger(Logger logger) { + super(logger.getName()); + this.logger = logger; + } + + public static SentryLogger getLogger(String name) { + return new SentryLogger(org.apache.log4j.Logger.getLogger(name)); + } + + public static SentryLogger getLoggerClass(Class clazz) { + return getLogger(clazz.getName()); + } + + public void setCaptureEvents(boolean captureEvents) { + this.captureEvents = captureEvents; + } + + @Override + public void error(Object message, Throwable t) { + logger.error(message, t); + recordException(t, message.toString()); + } + + public void error(Throwable cause) { + String fm = cause.getLocalizedMessage(); + logger.error(fm, cause); + recordException(cause, fm); + + } + + /** + * Record a given exception + * + * @param t The exception to record + * @param message Message associated with exception + * @return True if the exception was submitted to Sentry, false if not (i.e. + * disabled/filtered out) + */ + public boolean recordException(Throwable t, String message) { + + // Only submit when capturing events + if (!captureEvents) { + return false; + } + + // Using the Throwable description as a work around as it contains the missing parameter values. + // This is because the ExceptionInterface uses the detailMessage in the throwable and ignores the parameters list + // This originally resulting in "identity={0}" instead of "identity=wjst" + // Note: the ExceptionInterface is legacy code and is removed in the newer sentry versions. + Throwable p = new Throwable(t.toString(), t); + + // Initialize new event + EventBuilder eventBuilder = new EventBuilder() + .withSentryInterface(new ExceptionInterface(p)) + .withMessage(message) + .withTimestamp(new Date()); + + // Initialize event finger print to be: {exceptionclass} + StringBuilder fingerprint = new StringBuilder(t.getClass().getName()); + + // If possible, record error method and extend fingerprint with it + StackTraceElement[] stElems = t.getStackTrace(); + if (stElems != null && stElems.length > 0) { + StackTraceElement stElem = stElems[0]; + if (!isEmpty(stElem.getClassName()) && !isEmpty(stElem.getMethodName())) { + String errorMethod = stElem.getClassName() + '.' + stElem.getMethodName(); + eventBuilder.withTag("error_method", errorMethod); + fingerprint.append(":").append(errorMethod); + } + } + + eventBuilder.withFingerprint(fingerprint.toString()); + Event e = eventBuilder.getEvent(); + + // This is a hack to get around a Sentry bug - without it + // Sentry throws an NPE internally and quietly. The docs + // don't describe this object, so these values are somewhat + // arbitrary, but they work and are reflective of Sentry info + e.setSdk(new Sdk("java", "1.7", null)); + + // This sends an exception event to Sentry. + Sentry.capture(e); + return true; + } +} diff --git a/src/test/java/SentryLogger/SentryLoggerTest.java b/src/test/java/SentryLogger/SentryLoggerTest.java new file mode 100644 index 0000000..e04f96e --- /dev/null +++ b/src/test/java/SentryLogger/SentryLoggerTest.java @@ -0,0 +1,49 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package SentryLogger; + +import io.sentry.Sentry; +import java.io.IOException; +import org.crossref.refmatching.SentryLogger; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit test for SentryIoClient class. + * + * Tests confirm filtering behavior. + * + * @author jhanna + */ +public class SentryLoggerTest { + + private static final String TEST_MESSAGE = "TEST"; + private static final String EXCEPTION_MSG = "Testing Sentry exception"; + private final SentryLogger sentryLogger = SentryLogger.getLoggerClass(SentryLoggerTest.class); + private static Throwable ioExeception; + + @Before + public void initTest() { + ioExeception = new IOException(EXCEPTION_MSG); + sentryLogger.error(TEST_MESSAGE, ioExeception); + Sentry.init(); + } + + @Test + public void exceptionEventTest() { + sentryLogger.setCaptureEvents(true); + Assert.assertTrue("If capture is enabled, return should be true (submitted to Sentry)", + sentryLogger.recordException(ioExeception, TEST_MESSAGE)); + } + + @Test + public void exceptionNotCaptured() { + sentryLogger.setCaptureEvents(false); + Assert.assertFalse("If capture is not enabled, return should be false", + sentryLogger.recordException(ioExeception, TEST_MESSAGE)); + } +} -- GitLab From 7a7d37cf5db2c4793b840cb3377b49d036814e9d Mon Sep 17 00:00:00 2001 From: Jason Hanna Date: Wed, 14 Sep 2022 13:23:44 +0000 Subject: [PATCH 2/4] feat: Cr 510 branch changes --- .gitlab-ci.yml | 13 ------------- .../org/crossref/refmatching/ReferenceMatcher.java | 1 - .../org/crossref/refmatching/sentry.properties | 1 + src/test/java/SentryLogger/SentryLoggerTest.java | 13 +------------ 4 files changed, 2 insertions(+), 26 deletions(-) create mode 100644 src/main/resources/org/crossref/refmatching/sentry.properties diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05ae862..f0069fe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,19 +41,6 @@ release: - VERSION.txt - CHANGELOG.md -release: - image: registry.gitlab.com/crossref/infrastructure/aws-ecr-ecs-cicd-docker:latest - stage: release - script: - - npx semantic-release - only: - - develop - - main - artifacts: - paths: - - VERSION.txt - - CHANGELOG.md - deploy: stage: deploy image: maven:3.3.9-jdk-8 diff --git a/src/main/java/org/crossref/refmatching/ReferenceMatcher.java b/src/main/java/org/crossref/refmatching/ReferenceMatcher.java index a9bfa26..e151cf3 100644 --- a/src/main/java/org/crossref/refmatching/ReferenceMatcher.java +++ b/src/main/java/org/crossref/refmatching/ReferenceMatcher.java @@ -31,7 +31,6 @@ public class ReferenceMatcher { private final CandidateSelector selector; private final CandidateValidator validator = new CandidateValidator(); private static final Logger LOGGER = LogUtils.getLogger(); - private static final String DSN = "http://17a5b3ec5566440c8ed1945a710c10a6@sentry.io/1769602"; /** * Constructor sets apiClient. diff --git a/src/main/resources/org/crossref/refmatching/sentry.properties b/src/main/resources/org/crossref/refmatching/sentry.properties new file mode 100644 index 0000000..f45dc1c --- /dev/null +++ b/src/main/resources/org/crossref/refmatching/sentry.properties @@ -0,0 +1 @@ +dsn=http://17a5b3ec5566440c8ed1945a710c10a6@sentry.io/1769602 \ No newline at end of file diff --git a/src/test/java/SentryLogger/SentryLoggerTest.java b/src/test/java/SentryLogger/SentryLoggerTest.java index e04f96e..d2be3c3 100644 --- a/src/test/java/SentryLogger/SentryLoggerTest.java +++ b/src/test/java/SentryLogger/SentryLoggerTest.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package SentryLogger; import io.sentry.Sentry; @@ -12,13 +7,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -/** - * Unit test for SentryIoClient class. - * - * Tests confirm filtering behavior. - * - * @author jhanna - */ + public class SentryLoggerTest { private static final String TEST_MESSAGE = "TEST"; -- GitLab From eb7684453e47a6a68eeb1e701dc0e231651644b6 Mon Sep 17 00:00:00 2001 From: mochajh Date: Mon, 26 Sep 2022 10:04:31 -0400 Subject: [PATCH 3/4] CR-510: removing duplicate property --- sentry.properties | 1 - 1 file changed, 1 deletion(-) delete mode 100644 sentry.properties diff --git a/sentry.properties b/sentry.properties deleted file mode 100644 index f45dc1c..0000000 --- a/sentry.properties +++ /dev/null @@ -1 +0,0 @@ -dsn=http://17a5b3ec5566440c8ed1945a710c10a6@sentry.io/1769602 \ No newline at end of file -- GitLab From 227fbeb3d40d82db948b7bbe7dd1c99cd5aa77ee Mon Sep 17 00:00:00 2001 From: mochajh Date: Mon, 3 Oct 2022 17:39:53 -0400 Subject: [PATCH 4/4] CR-510: Updating sentry env --- .../org/crossref/refmatching/MainApp.java | 21 +++++++++++++++++- .../refmatching/ReferenceMatcher.java | 17 +++++++++++++- .../crossref/refmatching/sentry.properties | 1 - .../java/SentryLogger/SentryLoggerTest.java | 22 ++++++++++++++++++- 4 files changed, 57 insertions(+), 4 deletions(-) delete mode 100644 src/main/resources/org/crossref/refmatching/sentry.properties diff --git a/src/main/java/org/crossref/refmatching/MainApp.java b/src/main/java/org/crossref/refmatching/MainApp.java index 809bbbb..bb95227 100644 --- a/src/main/java/org/crossref/refmatching/MainApp.java +++ b/src/main/java/org/crossref/refmatching/MainApp.java @@ -1,9 +1,13 @@ package org.crossref.refmatching; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Properties; +import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -30,6 +34,8 @@ public class MainApp { private static final String DEFAULT_API_HOST = "api.crossref.org"; private static final int DEFAULT_API_PORT = 443; private final static String CRAPI_KEY_FILE = ".crapi_key"; + private final static String SENTRY_FILE = "sentry_dsn.properties"; + private final static String DSN = "dsn"; private static final String DEFAULT_DELIMITER = "\r?\n"; private static final Logger LOGGER = LogUtils.getLogger(); @@ -38,12 +44,25 @@ public class MainApp { private static int apiPort = DEFAULT_API_PORT; private static String apiKeyFile = System.getProperty("user.home") + "/" + CRAPI_KEY_FILE; + private static String sentryDSNFile = System.getProperty("user.home") + "/" + + SENTRY_FILE; private static String outputFileName = null; private static String delimiter = DEFAULT_DELIMITER; public static void main(String[] args) { try { MatchRequest request = processArgs(args); + String sentryDSN = ""; + try { + FileReader reader = new FileReader(sentryDSNFile); + Properties sentry = new Properties(); + sentry.load(reader); + sentryDSN = sentry.getProperty(DSN); + } catch (FileNotFoundException ex) { + LOGGER.info("No Sentry File Specified: " + ex.getMessage()); + } catch (IOException ex) { + LOGGER.info("Problem reading file: " + ex.getMessage()); + } // Initialize API client connector UnmanagedHttpClient httpClient = new UnmanagedHttpClient(apiScheme, @@ -53,7 +72,7 @@ public class MainApp { ICrossRefApiClient apiClient = new CrossRefApiHttpClient(httpClient); // Initialize matcher object - ReferenceMatcher matcher = new ReferenceMatcher(apiClient); + ReferenceMatcher matcher = new ReferenceMatcher(apiClient, sentryDSN); matcher.setCacheJournalAbbrevMap(true); matcher.initialize(); diff --git a/src/main/java/org/crossref/refmatching/ReferenceMatcher.java b/src/main/java/org/crossref/refmatching/ReferenceMatcher.java index e151cf3..8211793 100644 --- a/src/main/java/org/crossref/refmatching/ReferenceMatcher.java +++ b/src/main/java/org/crossref/refmatching/ReferenceMatcher.java @@ -31,6 +31,7 @@ public class ReferenceMatcher { private final CandidateSelector selector; private final CandidateValidator validator = new CandidateValidator(); private static final Logger LOGGER = LogUtils.getLogger(); + private String dsn = ""; /** * Constructor sets apiClient. @@ -40,6 +41,18 @@ public class ReferenceMatcher { public ReferenceMatcher(ICrossRefApiClient apiClient) { this.selector = new CandidateSelector(apiClient); } + + /** + * Constructor sets apiClient + * Constructor sets dsn sentry value + * + * @param apiClient CR API client implementation + * @param dsn sentry dsn value + */ + public ReferenceMatcher(ICrossRefApiClient apiClient, String dsn) { + this.selector = new CandidateSelector(apiClient); + this.dsn = dsn; + } /** * Set flag indicating whether or not to cache journal abbreviation map. @@ -64,7 +77,9 @@ public class ReferenceMatcher { * this just caches the journals depending on a flag. */ public void initialize() { - Sentry.init(); + if (!dsn.isEmpty()){ + Sentry.init(dsn); + } if (cacheJournalAbbrevMap) { try { InputStream is = ReferenceMatcher.class.getResourceAsStream( diff --git a/src/main/resources/org/crossref/refmatching/sentry.properties b/src/main/resources/org/crossref/refmatching/sentry.properties deleted file mode 100644 index f45dc1c..0000000 --- a/src/main/resources/org/crossref/refmatching/sentry.properties +++ /dev/null @@ -1 +0,0 @@ -dsn=http://17a5b3ec5566440c8ed1945a710c10a6@sentry.io/1769602 \ No newline at end of file diff --git a/src/test/java/SentryLogger/SentryLoggerTest.java b/src/test/java/SentryLogger/SentryLoggerTest.java index d2be3c3..bb51a6b 100644 --- a/src/test/java/SentryLogger/SentryLoggerTest.java +++ b/src/test/java/SentryLogger/SentryLoggerTest.java @@ -1,7 +1,10 @@ package SentryLogger; import io.sentry.Sentry; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; +import java.util.Properties; import org.crossref.refmatching.SentryLogger; import org.junit.Assert; import org.junit.Before; @@ -14,12 +17,29 @@ public class SentryLoggerTest { private static final String EXCEPTION_MSG = "Testing Sentry exception"; private final SentryLogger sentryLogger = SentryLogger.getLoggerClass(SentryLoggerTest.class); private static Throwable ioExeception; + private final static String SENTRY_FILE = "sentry_dsn.properties"; + private final static String DSN = "dsn"; + private static String sentryDSNFile = System.getProperty("user.home") + "/" + + SENTRY_FILE; @Before public void initTest() { ioExeception = new IOException(EXCEPTION_MSG); sentryLogger.error(TEST_MESSAGE, ioExeception); - Sentry.init(); + + String sentryDSN = ""; + try { + FileReader reader = new FileReader(sentryDSNFile); + Properties sentry = new Properties(); + sentry.load(reader); + sentryDSN = sentry.getProperty(DSN); + } catch (FileNotFoundException ex) { + System.out.println("No Sentry File Specified: " + ex.getMessage()); + } catch (IOException ex) { + System.out.println("Problem reading file: " + ex.getMessage()); + } + + Sentry.init(sentryDSN); } @Test -- GitLab