Skip to content

Commit a2ebaf9

Browse files
committed
appender
1 parent 04ca7f5 commit a2ebaf9

3 files changed

Lines changed: 210 additions & 1 deletion

File tree

bugsnag/src/main/java/com/bugsnag/BugsnagAppender.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.bugsnag.callbacks.Callback;
44
import com.bugsnag.delivery.Delivery;
55
import com.bugsnag.logback.BugsnagMarker;
6+
import com.bugsnag.logback.LogbackFeatureFlag;
67
import com.bugsnag.logback.LogbackMetadata;
78
import com.bugsnag.logback.LogbackMetadataKey;
89
import com.bugsnag.logback.LogbackMetadataTab;
@@ -74,9 +75,11 @@ public class BugsnagAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
7475

7576
/** Application version. */
7677
private String appVersion;
77-
7878
private List<LogbackMetadata> globalMetadata = new ArrayList<LogbackMetadata>();
7979

80+
/** Feature flags configured via logback.xml. */
81+
private List<LogbackFeatureFlag> featureFlags = new ArrayList<LogbackFeatureFlag>();
82+
8083
/** Bugsnag client. */
8184
private Bugsnag bugsnag = null;
8285

@@ -271,6 +274,11 @@ private Bugsnag createBugsnag() {
271274
bugsnag.setProjectPackages(projectPackages.toArray(new String[0]));
272275
bugsnag.setSendThreads(sendThreads);
273276

277+
// Add feature flags
278+
for (LogbackFeatureFlag flag : featureFlags) {
279+
bugsnag.addFeatureFlag(flag.getName(), flag.getVariant());
280+
}
281+
274282
// Add a callback to put global metadata on every report
275283
bugsnag.addCallback(new Callback() {
276284
@Override
@@ -592,4 +600,70 @@ private boolean isExcludedLogger(String loggerName) {
592600
}
593601
return false;
594602
}
603+
604+
/**
605+
* Add a feature flag with a name and variant.
606+
* This is typically configured via logback.xml.
607+
*
608+
* @param name the feature flag name
609+
* @param variant the feature flag variant (can be null)
610+
*/
611+
public void addFeatureFlag(String name, String variant) {
612+
LogbackFeatureFlag flag = new LogbackFeatureFlag();
613+
flag.setName(name);
614+
flag.setVariant(variant);
615+
featureFlags.add(flag);
616+
617+
if (bugsnag != null) {
618+
bugsnag.addFeatureFlag(name, variant);
619+
}
620+
}
621+
622+
/**
623+
* Add a feature flag with just a name (no variant).
624+
* This is typically configured via logback.xml.
625+
*
626+
* @param name the feature flag name
627+
*/
628+
public void addFeatureFlag(String name) {
629+
addFeatureFlag(name, null);
630+
}
631+
632+
/**
633+
* Add a feature flag from logback.xml configuration.
634+
* Internal use only - should only be used via the logback.xml file.
635+
*
636+
* @param flag the feature flag to add
637+
*/
638+
public void setFeatureFlag(LogbackFeatureFlag flag) {
639+
featureFlags.add(flag);
640+
641+
if (bugsnag != null) {
642+
bugsnag.addFeatureFlag(flag.getName(), flag.getVariant());
643+
}
644+
}
645+
646+
/**
647+
* Clear a feature flag by name.
648+
*
649+
* @param name the feature flag name to remove
650+
*/
651+
public void clearFeatureFlag(String name) {
652+
featureFlags.removeIf(flag -> flag.getName().equals(name));
653+
654+
if (bugsnag != null) {
655+
bugsnag.clearFeatureFlag(name);
656+
}
657+
}
658+
659+
/**
660+
* Clear all feature flags.
661+
*/
662+
public void clearFeatureFlags() {
663+
featureFlags.clear();
664+
665+
if (bugsnag != null) {
666+
bugsnag.clearFeatureFlags();
667+
}
668+
}
595669
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.bugsnag.logback;
2+
3+
/**
4+
* Used to allow feature flags to be configured in the logback.xml file.
5+
*/
6+
public class LogbackFeatureFlag {
7+
8+
private String name;
9+
private String variant;
10+
11+
/**
12+
* @return the name of the feature flag
13+
*/
14+
public String getName() {
15+
return name;
16+
}
17+
18+
/**
19+
* @param name the name of the feature flag
20+
*/
21+
public void setName(String name) {
22+
this.name = name;
23+
}
24+
25+
/**
26+
* @return the variant of the feature flag
27+
*/
28+
public String getVariant() {
29+
return variant;
30+
}
31+
32+
/**
33+
* @param variant the variant of the feature flag
34+
*/
35+
public void setVariant(String variant) {
36+
this.variant = variant;
37+
}
38+
}

bugsnag/src/test/java/com/bugsnag/AppenderTest.java

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public void swapDelivery() {
5656
originalSessionDelivery = bugsnag.getSessionDelivery();
5757
sessionDelivery = new StubSessionDelivery();
5858
bugsnag.setSessionDelivery(sessionDelivery);
59+
60+
// Clear any feature flags from previous tests
61+
appender.clearFeatureFlags();
5962
}
6063

6164
/**
@@ -414,4 +417,98 @@ private SessionTracker getSessionTracker(Bugsnag bugsnag) {
414417
private Map<String, Object> getMetadataMap(Notification notification, String key) {
415418
return ((Map<String, Object>) notification.getEvents().get(0).getMetadata().get(key));
416419
}
420+
421+
@Test
422+
public void testFeatureFlagConfiguration() {
423+
// Add feature flags programmatically (XML configuration will be tested separately)
424+
appender.addFeatureFlag("sample_group", "a");
425+
appender.addFeatureFlag("another_feature");
426+
427+
// Send a log message
428+
LOGGER.warn("Exception with feature flags", new RuntimeException("test"));
429+
430+
// Check that a report was sent to Bugsnag
431+
assertEquals(1, delivery.getNotifications().size());
432+
433+
Notification notification = delivery.getNotifications().get(0);
434+
List<FeatureFlag> featureFlags = notification.getEvents().get(0).getFeatureFlags();
435+
436+
// Check that feature flags are present
437+
assertEquals(2, featureFlags.size());
438+
439+
// Check first feature flag
440+
assertEquals("sample_group", featureFlags.get(0).getName());
441+
assertEquals("a", featureFlags.get(0).getVariant());
442+
443+
// Check second feature flag
444+
assertEquals("another_feature", featureFlags.get(1).getName());
445+
assertEquals(null, featureFlags.get(1).getVariant());
446+
}
447+
448+
@Test
449+
public void testAddFeatureFlagProgrammatically() {
450+
// Add a feature flag programmatically
451+
appender.addFeatureFlag("runtime_feature", "variant_b");
452+
453+
// Send a log message
454+
LOGGER.warn("Exception with runtime feature flag", new RuntimeException("test"));
455+
456+
// Check that a report was sent to Bugsnag
457+
assertEquals(1, delivery.getNotifications().size());
458+
459+
Notification notification = delivery.getNotifications().get(0);
460+
List<FeatureFlag> featureFlags = notification.getEvents().get(0).getFeatureFlags();
461+
462+
// Should have 1 programmatic feature flag
463+
assertEquals(1, featureFlags.size());
464+
465+
// Check the programmatically added flag is present
466+
assertEquals("runtime_feature", featureFlags.get(0).getName());
467+
assertEquals("variant_b", featureFlags.get(0).getVariant());
468+
}
469+
470+
@Test
471+
public void testClearFeatureFlag() {
472+
// Add some feature flags first
473+
appender.addFeatureFlag("sample_group", "a");
474+
appender.addFeatureFlag("another_feature");
475+
476+
// Clear a specific feature flag
477+
appender.clearFeatureFlag("sample_group");
478+
479+
// Send a log message
480+
LOGGER.warn("Exception after clearing feature flag", new RuntimeException("test"));
481+
482+
// Check that a report was sent to Bugsnag
483+
assertEquals(1, delivery.getNotifications().size());
484+
485+
Notification notification = delivery.getNotifications().get(0);
486+
List<FeatureFlag> featureFlags = notification.getEvents().get(0).getFeatureFlags();
487+
488+
// Should only have 1 feature flag (another_feature) remaining
489+
assertEquals(1, featureFlags.size());
490+
assertEquals("another_feature", featureFlags.get(0).getName());
491+
}
492+
493+
@Test
494+
public void testClearAllFeatureFlags() {
495+
// Add some feature flags first
496+
appender.addFeatureFlag("sample_group", "a");
497+
appender.addFeatureFlag("another_feature");
498+
499+
// Clear all feature flags
500+
appender.clearFeatureFlags();
501+
502+
// Send a log message
503+
LOGGER.warn("Exception after clearing all feature flags", new RuntimeException("test"));
504+
505+
// Check that a report was sent to Bugsnag
506+
assertEquals(1, delivery.getNotifications().size());
507+
508+
Notification notification = delivery.getNotifications().get(0);
509+
List<FeatureFlag> featureFlags = notification.getEvents().get(0).getFeatureFlags();
510+
511+
// Should have no feature flags
512+
assertEquals(0, featureFlags.size());
513+
}
417514
}

0 commit comments

Comments
 (0)