omnis-logger-sdk
Version:
SDK для интеграции Omnis Logger в проекты
781 lines (627 loc) • 23.6 kB
Markdown
# Omnis Logger SDK - React Native Guide
Подробное руководство по интеграции Omnis Logger SDK в React Native приложения.
## Установка
```bash
npm install @your-org/omnis-logger react-native-device-info
```
## Быстрый старт
### 1. Простая инициализация (рекомендуемый способ)
Самый простой способ - передать модуль `DeviceInfo` в SDK, и он сам соберет все нужные данные:
```javascript
// Logger.js
import { initOmnisLogger } from '@your-org/omnis-logger';
import DeviceInfo from 'react-native-device-info';
import { Platform } from 'react-native';
class LoggerService {
constructor() {
this.logger = null;
this.isInitialized = false;
}
initialize() {
if (this.isInitialized) return this.logger;
try {
// Простая инициализация - SDK сам соберет все данные
this.logger = initOmnisLogger({
endpoint: 'https://your-api.com',
apiKey: 'your-api-key',
environment: __DEV__ ? 'development' : 'production',
platform: Platform.OS,
version: DeviceInfo.getVersion(),
userId: 'user-123', // или получите из хранилища
sessionId: `session-${Date.now()}`,
deviceInfoModule: DeviceInfo // Передаем модуль, SDK сам соберет данные
});
this.isInitialized = true;
console.log('Logger инициализирован успешно');
return this.logger;
} catch (error) {
console.error('Ошибка инициализации логгера:', error);
return null;
}
}
getLogger() {
return this.logger;
}
// Удобные методы для логирования
error(message, context = {}) {
if (this.logger) {
this.logger.error(message, context);
} else if (__DEV__) {
console.error('[Logger не инициализирован]', message, context);
}
}
info(message, context = {}) {
if (this.logger) {
this.logger.info(message, context);
} else if (__DEV__) {
console.info('[Logger не инициализирован]', message, context);
}
}
exception(error, context = {}) {
if (this.logger) {
this.logger.exception(error, context);
} else if (__DEV__) {
console.error('[Logger не инициализирован]', error, context);
}
}
}
export default new LoggerService();
```
### 2. Инициализация в App.js
```javascript
import React, { useEffect } from 'react';
import { AppState } from 'react-native';
import Logger from './src/services/Logger';
const App = () => {
useEffect(() => {
// Инициализируем логгер при запуске приложения (синхронно!)
const logger = Logger.initialize();
if (logger) {
Logger.info('Приложение запущено');
}
// Логируем изменения состояния приложения
const handleAppStateChange = (nextAppState) => {
Logger.info('Изменение состояния приложения', {
state: nextAppState
});
};
const subscription = AppState.addEventListener(
'change',
handleAppStateChange
);
return () => {
subscription?.remove();
Logger.info('Приложение закрыто');
};
}, []);
return (
// Ваше приложение
);
};
export default App;
```
### Что происходит автоматически
SDK автоматически соберет:
**Синхронные данные** (мгновенно):
- `deviceId`, `model`, `brand`
- `systemName`, `systemVersion`
- `bundleId`, `version`, `buildNumber`
- `isTablet`, `deviceType`
- `hasNotch`, `hasDynamicIsland` (iOS)
**Асинхронные данные** (в фоне):
- `deviceName`, `manufacturer`
- `isEmulator`, `uniqueId`
- `androidId`, `apiLevel` (Android)
- `screenWidth`, `screenHeight`, `pixelRatio`
- `networkType`, `isConnected` (если установлен NetInfo)
### 3. Расширенная инициализация (если нужен контроль)
Если вам нужен полный контроль над сбором данных:
```javascript
// ExtendedLogger.js
import { initOmnisLogger } from '@your-org/omnis-logger';
import DeviceInfo from 'react-native-device-info';
import { Platform } from 'react-native';
class ExtendedLoggerService {
constructor() {
this.logger = null;
this.isInitialized = false;
}
async initialize() {
if (this.isInitialized) return this.logger;
try {
// Собираем критически важные данные сами
const essentialDeviceInfo = {
platform: Platform.OS,
deviceId: DeviceInfo.getDeviceId(),
model: DeviceInfo.getModel(),
systemName: DeviceInfo.getSystemName(),
systemVersion: DeviceInfo.getSystemVersion(),
version: DeviceInfo.getVersion(),
isTablet: DeviceInfo.isTablet(),
// Добавляем кастомные поля
customAppData: 'some-custom-value',
userSegment: 'premium',
};
// Инициализируем с готовыми данными
this.logger = initOmnisLogger({
endpoint: 'https://your-api.com',
apiKey: 'your-api-key',
environment: __DEV__ ? 'development' : 'production',
platform: Platform.OS,
version: DeviceInfo.getVersion(),
userId: 'user-123',
sessionId: `session-${Date.now()}`,
deviceInfo: essentialDeviceInfo // Используем готовые данные
});
// Дополнительные данные собираем отдельно
this.collectAdditionalInfo();
this.isInitialized = true;
return this.logger;
} catch (error) {
console.error('Ошибка инициализации логгера:', error);
return null;
}
}
async collectAdditionalInfo() {
try {
const additionalInfo = {
deviceName: await DeviceInfo.getDeviceName(),
manufacturer: await DeviceInfo.getManufacturer(),
isEmulator: await DeviceInfo.isEmulator(),
};
// Обновляем данные об устройстве
if (this.logger) {
this.logger.updateDeviceInfo(additionalInfo);
}
} catch (error) {
console.warn('Ошибка сбора дополнительных данных:', error);
}
}
}
export default new ExtendedLoggerService();
```
## Сравнение подходов
| Критерий | deviceInfoModule | deviceInfo |
|----------|------------------|------------|
| **Простота** | ✅ Очень просто | ❌ Много кода |
| **Скорость инициализации** | ✅ Мгновенно | ❌ Ждем async |
| **Контроль** | ❌ Ограниченный | ✅ Полный |
| **Кастомные поля** | ❌ Нет | ✅ Да |
| **Оптимизация** | ✅ SDK оптимизирован | ❌ Ваша ответственность |
**Рекомендация**: Используйте `deviceInfoModule` для большинства случаев, `deviceInfo` только если нужны кастомные поля.
### 4. Использование в компонентах
#### Пример экрана профиля
```javascript
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import Logger from '../services/Logger'; // Используем наш упрощенный сервис
const ProfileScreen = ({ navigation, route }) => {
const [isLoading, setIsLoading] = useState(false);
const [profile, setProfile] = useState(null);
useEffect(() => {
Logger.info('Экран профиля открыт', {
screen: 'Profile',
userId: route.params?.userId
});
loadProfile();
return () => {
Logger.info('Экран профиля закрыт', {
screen: 'Profile'
});
};
}, []);
const loadProfile = async () => {
setIsLoading(true);
try {
const response = await fetch('/api/profile');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
setProfile(data);
Logger.info('Профиль загружен успешно', {
screen: 'Profile',
profileId: data.id
});
} catch (error) {
Logger.exception(error, {
screen: 'Profile',
action: 'loadProfile'
});
Alert.alert('Ошибка', 'Не удалось загрузить профиль');
} finally {
setIsLoading(false);
}
};
// Остальной код компонента...
return (
<View>
<Text>Профиль</Text>
{/* UI компонента */}
</View>
);
};
export default ProfileScreen;
```
#### Простая инициализация в реальном проекте
```javascript
// services/Logger.js
import { initOmnisLogger } from '@your-org/omnis-logger';
import DeviceInfo from 'react-native-device-info';
import { Platform } from 'react-native';
import { storage } from './storage'; // ваше хранилище
class LoggerService {
constructor() {
this.logger = null;
}
initialize() {
if (this.logger) return this.logger;
const userId = storage.getString('USER_ID') || 'anonymous';
const sessionId = `session-${Date.now()}`;
// Вся логика сбора данных теперь в SDK!
this.logger = initOmnisLogger({
endpoint: 'https://your-api.com',
apiKey: 'your-api-key',
environment: __DEV__ ? 'development' : 'production',
platform: Platform.OS,
version: DeviceInfo.getVersion(),
userId: userId,
sessionId: sessionId,
deviceInfoModule: DeviceInfo // SDK сам соберет все данные
});
return this.logger;
}
// Простые методы для удобства
error = (message, context) => this.logger?.error(message, context);
info = (message, context) => this.logger?.info(message, context);
exception = (error, context) => this.logger?.exception(error, context);
}
export default new LoggerService();
```
Вместо 100+ строк кода для сбора данных, теперь всего несколько строк!
## Практические примеры
### Обработка аутентификации
```javascript
// AuthService.js
import Logger from './Logger';
class AuthService {
async login(email, password) {
try {
Logger.info('Попытка входа', { email });
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!response.ok) {
throw new Error(`Ошибка входа: ${response.status}`);
}
const data = await response.json();
// Обновляем пользователя в логгере
const logger = Logger.getLogger();
if (logger) {
logger.setUser(data.user.id);
logger.setSession(data.sessionId);
}
Logger.info('Пользователь вошел в систему', {
userId: data.user.id,
email: data.user.email
});
return data;
} catch (error) {
Logger.exception(error, {
action: 'login',
email
});
throw error;
}
}
}
export default new AuthService();
```
### Логирование производительности
```javascript
// PerformanceLogger.js
import Logger from './Logger';
class PerformanceLogger {
startTimer(name) {
const startTime = Date.now();
return {
end: (context = {}) => {
const duration = Date.now() - startTime;
Logger.info(`Производительность: ${name}`, {
...context,
type: 'performance',
operation: name,
duration: `${duration}ms`
});
}
};
}
logScreenLoad(screenName) {
const timer = this.startTimer(`Screen Load: ${screenName}`);
return timer;
}
}
export default new PerformanceLogger();
```
## Лучшие практики
### 1. Оптимизация сбора данных об устройстве
```javascript
// Быстрая инициализация с минимальными данными
class OptimizedLoggerService extends LoggerService {
async quickInitialize() {
if (this.isInitialized) return this.logger;
try {
// Собираем только самые важные синхронные данные
const essentialInfo = {
platform: 'react-native',
deviceId: DeviceInfo.getDeviceId(),
model: DeviceInfo.getModel(),
systemName: DeviceInfo.getSystemName(),
systemVersion: DeviceInfo.getSystemVersion(),
version: DeviceInfo.getVersion(),
isTablet: DeviceInfo.isTablet(),
};
// Инициализируем логгер немедленно
this.logger = initOmnisLogger({
endpoint: 'https://your-api.com',
apiKey: 'your-api-key',
environment: __DEV__ ? 'development' : 'production',
platform: 'react-native',
version: DeviceInfo.getVersion(),
deviceInfo: essentialInfo
});
this.isInitialized = true;
// Собираем дополнительные данные в фоне
this.collectAdditionalInfo();
return this.logger;
} catch (error) {
console.error('Ошибка быстрой инициализации логгера:', error);
return null;
}
}
async collectAdditionalInfo() {
try {
// Собираем дополнительные данные асинхронно
const additionalInfo = {
deviceName: await DeviceInfo.getDeviceName(),
manufacturer: await DeviceInfo.getManufacturer(),
isEmulator: await DeviceInfo.isEmulator(),
uniqueId: await DeviceInfo.getUniqueId(),
};
// Android специфичные данные
if (DeviceInfo.getSystemName() === 'Android') {
additionalInfo.androidId = await DeviceInfo.getAndroidId();
additionalInfo.apiLevel = await DeviceInfo.getApiLevel();
}
// iOS специфичные данные
if (DeviceInfo.getSystemName() === 'iOS') {
additionalInfo.hasNotch = DeviceInfo.hasNotch();
additionalInfo.hasDynamicIsland = DeviceInfo.hasDynamicIsland();
}
// Обновляем данные об устройстве в логгере
if (this.logger) {
this.logger.updateDeviceInfo(additionalInfo);
}
} catch (error) {
console.warn('Ошибка сбора дополнительных данных:', error);
}
}
}
export default new OptimizedLoggerService();
```
### 2. Полезные данные для UI Dashboard
```javascript
// Функция для сбора данных специально для отображения в UI
const getUIDisplayInfo = async () => {
const uiInfo = {};
try {
// Критически важные данные для техподдержки
uiInfo.deviceName = await DeviceInfo.getDeviceName();
uiInfo.model = DeviceInfo.getModel();
uiInfo.manufacturer = await DeviceInfo.getManufacturer();
uiInfo.systemName = DeviceInfo.getSystemName();
uiInfo.systemVersion = DeviceInfo.getSystemVersion();
uiInfo.appVersion = DeviceInfo.getVersion();
uiInfo.buildNumber = DeviceInfo.getBuildNumber();
// Полезно для диагностики
uiInfo.isEmulator = await DeviceInfo.isEmulator();
uiInfo.isTablet = DeviceInfo.isTablet();
// Android специфичные данные для техподдержки
if (DeviceInfo.getSystemName() === 'Android') {
uiInfo.apiLevel = await DeviceInfo.getApiLevel();
uiInfo.androidId = await DeviceInfo.getAndroidId();
}
// iOS специфичные данные
if (DeviceInfo.getSystemName() === 'iOS') {
uiInfo.hasNotch = DeviceInfo.hasNotch();
uiInfo.hasDynamicIsland = DeviceInfo.hasDynamicIsland();
}
// Размер экрана для понимания UX проблем
const { Dimensions } = require('react-native');
const dimensions = Dimensions.get('window');
uiInfo.screenWidth = dimensions.width;
uiInfo.screenHeight = dimensions.height;
uiInfo.screenScale = dimensions.scale;
// Сетевая информация (если доступна)
try {
const netInfo = require('@react-native-netinfo/netinfo');
const networkState = await netInfo.fetch();
uiInfo.networkType = networkState.type;
uiInfo.isConnected = networkState.isConnected;
} catch (e) {
// NetInfo не установлен
}
} catch (error) {
console.error('Ошибка сбора UI данных:', error);
}
return uiInfo;
};
// Использование в приложении
const App = () => {
useEffect(() => {
const initializeWithUIData = async () => {
const deviceInfo = await getUIDisplayInfo();
const logger = initOmnisLogger({
endpoint: 'https://your-api.com',
apiKey: 'your-api-key',
environment: __DEV__ ? 'development' : 'production',
platform: 'react-native',
version: DeviceInfo.getVersion(),
deviceInfo: deviceInfo
});
// Логируем успешную инициализацию с основной информацией
logger.info('Приложение запущено', {
device: `${deviceInfo.manufacturer} ${deviceInfo.model}`,
os: `${deviceInfo.systemName} ${deviceInfo.systemVersion}`,
app: `${deviceInfo.appVersion} (${deviceInfo.buildNumber})`,
screen: `${deviceInfo.screenWidth}x${deviceInfo.screenHeight}`,
isEmulator: deviceInfo.isEmulator
});
};
initializeWithUIData();
}, []);
return <YourApp />;
};
```
### 3. Структура контекста
```javascript
// Всегда используйте структурированный контекст
Logger.error('Ошибка сети', {
screen: 'ProfileScreen',
action: 'loadProfile',
endpoint: '/api/profile',
statusCode: 500,
retry_attempt: 2
});
```
### 4. Логирование пользовательских действий
```javascript
const handleButtonPress = () => {
Logger.info('Пользователь нажал кнопку', {
screen: 'HomeScreen',
button: 'startOrder',
user_action: true
});
// ваша логика
};
```
### 5. Обработка ошибок
```javascript
try {
// ваш код
} catch (error) {
Logger.exception(error, {
screen: 'CurrentScreen',
action: 'specific_action',
additional_context: 'any_useful_info'
});
// показать пользователю дружелюбное сообщение
}
```
### 6. Условное логирование
```javascript
// Логирование только в определенных условиях
if (isImportantOperation) {
Logger.info('Выполняется важная операция', context);
}
// Различные уровни для разных окружений
if (__DEV__) {
Logger.debug('Отладочная информация', debugContext);
} else {
Logger.info('Продакшн информация', prodContext);
}
```
## Данные особенно полезные для UI Dashboard
При создании dashboard для отображения ошибок, следующие поля из DeviceInfo особенно полезны:
### Критически важные для техподдержки:
- `deviceName` - "iPhone 12 Pro", "Samsung Galaxy S21"
- `manufacturer` - "Apple", "Samsung"
- `model` - "iPhone14,3", "SM-G991B"
- `systemName` + `systemVersion` - "iOS 16.4", "Android 13"
- `appVersion` + `buildNumber` - "1.2.3 (456)"
- `isEmulator` - помогает понять тестовая ли это ошибка
### Полезные для диагностики UX проблем:
- `screenWidth` x `screenHeight` - размер экрана
- `isTablet` - тип устройства
- `hasNotch` / `hasDynamicIsland` - для iOS layout проблем
- `networkType` - "wifi", "cellular" для сетевых ошибок
### Пример отображения в UI:
```
📱 Device: iPhone 12 Pro (Apple)
🔧 System: iOS 16.4
📦 App: 1.2.3 (456)
📏 Screen: 390x844
🌐 Network: wifi
```
## Troubleshooting
### Проблема: Logger не инициализируется
```javascript
// Проверьте инициализацию
if (!Logger.getLogger()) {
console.warn('Logger не инициализирован');
// Попробуйте переинициализировать
Logger.initialize();
}
```
### Проблема: Отсутствуют данные об устройстве
```javascript
// Убедитесь что react-native-device-info установлен
// npm install react-native-device-info
// Для старых версий React Native может потребоваться linking
// react-native link react-native-device-info
// Проверьте что модуль передается правильно
const logger = initOmnisLogger({
// ... другие настройки
deviceInfoModule: DeviceInfo // Убедитесь что импорт корректный
});
```
### Проблема: Логи не отправляются
```javascript
// Принудительная отправка логов
const logger = Logger.getLogger();
if (logger) {
await logger.flush();
}
```
### Проблема: Нужны дополнительные поля DeviceInfo
```javascript
// Если SDK не собирает нужные вам поля, используйте расширенный подход
const customDeviceInfo = {
// Базовые поля
platform: Platform.OS,
deviceId: DeviceInfo.getDeviceId(),
// ... другие поля
// Ваши кастомные поля
customField: 'custom-value',
userSegment: 'premium'
};
const logger = initOmnisLogger({
// ... другие настройки
deviceInfo: customDeviceInfo // Используйте готовый объект
});
```
## Миграция с предыдущих версий
### Было (сложно):
```javascript
// Много кода для сбора данных
const deviceInfo = {
platform: Platform.OS,
deviceId: DeviceInfo.getDeviceId(),
deviceName: await DeviceInfo.getDeviceName(),
// ... еще 20+ полей
};
const logger = initOmnisLogger({
// ... настройки
deviceInfo: deviceInfo
});
```
### Стало (просто):
```javascript
// SDK сам соберет все данные
const logger = initOmnisLogger({
// ... настройки
deviceInfoModule: DeviceInfo
});
```