@natify/error-reporting-sentry
v1.0.2
Published
Error Reporting adapter for Natify Framework using Sentry.
Readme
@natify/error-reporting-sentry
Error Reporting adapter for Natify Framework using Sentry.
Installation
pnpm add @natify/error-reporting-sentry @sentry/react-nativeAutomatic Configuration
Sentry provides a wizard to automatically configure the project:
npx @sentry/wizard@latest -i reactNativeThis command:
- Installs necessary dependencies
- Configures iOS (CocoaPods)
- Configures Android
- Creates configuration files
Manual Configuration
iOS
cd ios && pod install && cd ..Android
No additional configuration required after running the wizard.
Configuration
Get Sentry DSN
- Create an account on Sentry
- Create a new project (React Native)
- Copy the DSN from Settings → Client Keys (DSN)
Usage
Basic Configuration
import { NatifyProvider } from "@natify/core";
import { SentryErrorReportingAdapter } from "@natify/error-reporting-sentry";
const errorReporting = new SentryErrorReportingAdapter({
dsn: "YOUR_SENTRY_DSN",
environment: __DEV__ ? "development" : "production",
release: "1.0.0",
});
// Initialize
await errorReporting.init();
const config = {
errorReporting: errorReporting,
// ... other adapters
};
function App() {
return (
<NatifyProvider config={config}>
<MyApp />
</NatifyProvider>
);
}Advanced Configuration
const errorReporting = new SentryErrorReportingAdapter({
dsn: "YOUR_SENTRY_DSN",
environment: "production",
release: "1.0.0",
debug: false, // Enable debug logs
enableTracing: true, // Performance monitoring
tracesSampleRate: 0.1, // 10% of transactions
attachScreenshot: true, // Capture screenshots on errors
attachViewHierarchy: true, // Capture view hierarchy
});Capturing Errors
Capture Exception
import { useAdapter, ErrorReportingPort, SeverityLevel } from "@natify/core";
function MyComponent() {
const errorReporting = useAdapter<ErrorReportingPort>("errorReporting");
const handleError = async () => {
try {
await riskyOperation();
} catch (error) {
// Capture error with context
errorReporting.captureException(error as Error, {
screen: "ProductDetail",
productId: "123",
action: "purchase",
});
}
};
}Capture Message
// Informative message
errorReporting.captureMessage("User completed checkout", SeverityLevel.INFO, {
orderId: "123",
total: 99.99,
});
// Warning
errorReporting.captureMessage("API response time is slow", SeverityLevel.WARNING, {
endpoint: "/api/products",
responseTime: 5000,
});
// Critical error
errorReporting.captureMessage("Payment gateway unavailable", SeverityLevel.ERROR, {
gateway: "stripe",
retryCount: 3,
});With NatifyError
import { NatifyError, NatifyErrorCode } from "@natify/core";
try {
await operation();
} catch (error) {
if (error instanceof NatifyError) {
errorReporting.captureException(error, {
errorCode: error.code,
context: error.context,
});
} else {
errorReporting.captureException(error as Error);
}
}User Context
Set User
import { useAdapter, ErrorReportingPort } from "@natify/core";
function useAuth() {
const errorReporting = useAdapter<ErrorReportingPort>("errorReporting");
const login = async (user: User) => {
// Set user context
errorReporting.setUser({
id: user.id,
email: user.email,
username: user.name,
plan: user.plan,
roles: user.roles,
});
};
const logout = async () => {
// Clear context
errorReporting.clearUser();
};
return { login, logout };
}Get Current User
const currentUser = errorReporting.getUser();
if (currentUser) {
console.log("Current user:", currentUser.id);
}Breadcrumbs
Add Breadcrumbs
// Before a critical operation
errorReporting.addBreadcrumb({
message: "User clicked checkout button",
category: "user-action",
level: SeverityLevel.INFO,
data: {
screen: "Cart",
itemsCount: 3,
},
});
// During an operation
errorReporting.addBreadcrumb({
message: "Calling payment API",
category: "api",
level: SeverityLevel.INFO,
data: {
endpoint: "/api/payment",
method: "POST",
},
});
// If there's an error, breadcrumbs help understand what happened before
try {
await processPayment();
} catch (error) {
errorReporting.captureException(error as Error);
// Previous breadcrumbs are automatically included
}Automatic Breadcrumbs
Breadcrumbs are automatically added for:
- Navigation between screens
- HTTP calls (if configured)
- User interactions (clicks, etc.)
Tags and Context
Set Tags
// Tags to filter errors in dashboard
errorReporting.setTags({
app_version: "1.0.0",
platform: Platform.OS,
build_number: "123",
feature: "checkout",
});
// Individual tag
errorReporting.setTag("user_plan", "premium");Set Context
// Application context
errorReporting.setContext("app", {
version: "1.0.0",
build: "123",
environment: "production",
});
// Device context
errorReporting.setContext("device", {
model: Device.modelName,
os: Platform.OS,
osVersion: Platform.Version,
});
// Navigation context
errorReporting.setContext("navigation", {
currentScreen: "ProductDetail",
previousScreen: "ProductList",
stackDepth: 3,
});Common Use Cases
Global Error Boundary
import { ErrorBoundary } from "@sentry/react-native";
import { NatifyProvider } from "@natify/core";
function App() {
const errorReporting = new SentryErrorReportingAdapter({
dsn: "YOUR_SENTRY_DSN",
});
await errorReporting.init();
return (
<ErrorBoundary>
<NatifyProvider config={{ errorReporting, ...otherAdapters }}>
<MyApp />
</NatifyProvider>
</ErrorBoundary>
);
}Capture Errors in UseCase
import { ErrorReportingPort, HttpClientPort } from "@natify/core";
export class PurchaseProductUseCase {
constructor(
private readonly http: HttpClientPort,
private readonly errorReporting: ErrorReportingPort
) {}
async execute(productId: string): Promise<void> {
// Add breadcrumb before operation
this.errorReporting.addBreadcrumb({
message: "Starting purchase",
category: "business-logic",
data: { productId },
});
try {
const response = await this.http.post("/purchase", { productId });
this.errorReporting.addBreadcrumb({
message: "Purchase successful",
category: "business-logic",
data: { orderId: response.data.orderId },
});
} catch (error) {
// Capture error with full context
this.errorReporting.captureException(error as Error, {
useCase: "PurchaseProduct",
productId,
step: "api-call",
});
throw error;
}
}
}Performance Tracking
const trackPerformance = async (operation: string, fn: () => Promise<void>) => {
const errorReporting = useAdapter<ErrorReportingPort>("errorReporting");
const startTime = Date.now();
try {
await fn();
const duration = Date.now() - startTime;
// If too slow, report as warning
if (duration > 5000) {
errorReporting.captureMessage(
`Slow operation: ${operation}`,
SeverityLevel.WARNING,
{
operation,
duration_ms: duration,
}
);
}
} catch (error) {
errorReporting.captureException(error as Error, {
operation,
duration_ms: Date.now() - startTime,
});
}
};Integration with BaseViewModel
import { useBaseViewModel } from "@natify/core";
import { useAdapter, ErrorReportingPort } from "@natify/core";
function useMyViewModel() {
const [baseState, { execute }] = useBaseViewModel();
const errorReporting = useAdapter<ErrorReportingPort>("errorReporting");
const loadData = useCallback(async () => {
const result = await execute(async () => {
// If there's an error, BaseViewModel captures it
// But we can add additional context
try {
return await fetchData();
} catch (error) {
errorReporting.captureException(error as Error, {
screen: "MyScreen",
action: "loadData",
});
throw error;
}
});
}, [execute, errorReporting]);
return { state: baseState, actions: { loadData } };
}Capture Network Errors
import { NatifyError, NatifyErrorCode } from "@natify/core";
// In an HTTP interceptor
const httpAdapter = new AxiosHttpAdapter("https://api.example.com", {}, {
onResponseError: async (error) => {
if (error.response?.status >= 500) {
// Capture server errors
errorReporting.captureException(error, {
endpoint: error.config?.url,
status: error.response?.status,
method: error.config?.method,
});
}
return Promise.reject(error);
},
});Module Integration
import { createModule } from "@natify/core";
import { PurchaseProductUseCase } from "./usecases/PurchaseProductUseCase";
export const ProductsModule = createModule("products", "Products")
.requires("error-reporting", "http")
.useCase("purchaseProduct", (adapters) => {
return new PurchaseProductUseCase(adapters.http, adapters.errorReporting);
})
.build();API
ErrorReportingPort
| Method | Description |
|--------|-------------|
| init(dsn?, options?) | Initializes the service |
| captureException(error, context?, level?) | Captures an exception |
| captureMessage(message, level?, context?) | Captures a message |
| setUser(user) | Sets user context |
| getUser() | Gets user context |
| addBreadcrumb(breadcrumb) | Adds breadcrumb |
| setTags(tags) | Sets tags |
| setTag(key, value) | Sets a tag |
| setContext(key, context) | Sets context |
| clearUser() | Clears user context |
| clearBreadcrumbs() | Clears breadcrumbs |
SeverityLevel
enum SeverityLevel {
FATAL = 'fatal', // Critical error causing crash
ERROR = 'error', // Error requiring attention
WARNING = 'warning', // Warning
INFO = 'info', // Information
DEBUG = 'debug', // Debug
}UserContext
interface UserContext {
id: string;
email?: string;
username?: string;
[key: string]: unknown;
}Breadcrumb
interface Breadcrumb {
message: string;
category?: string;
level?: SeverityLevel;
data?: Record<string, unknown>;
timestamp?: Date;
}Best Practices
Don't Capture Sensitive Information
❌ Avoid:
errorReporting.captureException(error, {
password: userPassword, // ❌ NEVER
credit_card: cardNumber, // ❌ NEVER
ssn: userSSN, // ❌ NEVER
});✅ Use:
errorReporting.captureException(error, {
user_id: userId, // ✅ OK
action: "checkout", // ✅ OK
screen: "Payment", // ✅ OK
});Add Relevant Context
// Always include useful context
errorReporting.captureException(error, {
screen: currentScreen,
action: "purchase",
productId: productId,
userId: currentUserId,
appVersion: "1.0.0",
});Use Breadcrumbs for Debugging
// Add breadcrumbs before critical operations
errorReporting.addBreadcrumb({
message: "User entered payment details",
category: "user-flow",
data: { screen: "Payment" },
});
errorReporting.addBreadcrumb({
message: "Validating payment",
category: "business-logic",
});
// If there's an error, you'll see the entire flow in SentrySet User After Login
// Immediately after successful login
errorReporting.setUser({
id: user.id,
email: user.email,
username: user.name,
});Notes
- Initialization: Always call
init()before using the adapter - DSN: Get the DSN from Sentry dashboard
- Environment: Use different DSNs or environments for dev/staging/prod
- Release: Set the version for release tracking
- Performance: Enable
enableTracingfor performance monitoring - Screenshots:
attachScreenshotautomatically captures screenshots on errors - iOS: Requires
pod installafter installation - Android: Wizard configures automatically
