or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ai-integrations.mdclient-management.mdcontext-management.mdevent-tracking.mdfeature-flags.mdindex.mduser-group-management.md

context-management.mddocs/

0

# Context Management

1

2

Thread-safe context system for automatic user identification, session tracking, and property tagging. PostHog's context management enables consistent user tracking across related operations while providing automatic exception capture and nested context support.

3

4

## Capabilities

5

6

### Context Creation

7

8

Create isolated contexts for grouping related operations with automatic cleanup and exception handling.

9

10

```python { .api }

11

def new_context(fresh: bool = False, capture_exceptions: bool = True):

12

"""

13

Create a new context scope that will be active for the duration of the with block.

14

15

Parameters:

16

- fresh: bool - Whether to start with a fresh context (default: False)

17

- capture_exceptions: bool - Whether to capture exceptions raised within the context (default: True)

18

19

Returns:

20

Context manager for use with 'with' statement

21

22

Notes:

23

- Creates isolated scope for user identification and tags

24

- Automatically captures exceptions if enabled

25

- Context data is inherited by child contexts unless fresh=True

26

- Thread-safe for concurrent operations

27

"""

28

```

29

30

### Function-Level Contexts

31

32

Apply context management to individual functions using decorators for automatic scope management.

33

34

```python { .api }

35

def scoped(fresh: bool = False, capture_exceptions: bool = True):

36

"""

37

Decorator that creates a new context for the function.

38

39

Parameters:

40

- fresh: bool - Whether to start with a fresh context (default: False)

41

- capture_exceptions: bool - Whether to capture and track exceptions with posthog error tracking (default: True)

42

43

Returns:

44

Function decorator

45

46

Notes:

47

- Automatically creates context for decorated function

48

- Cleans up context when function exits

49

- Preserves function return values and exceptions

50

- Suitable for background tasks, request handlers, etc.

51

"""

52

```

53

54

### User Identification

55

56

Associate contexts with specific users for automatic user tracking across all operations.

57

58

```python { .api }

59

def identify_context(distinct_id: str):

60

"""

61

Identify the current context with a distinct ID.

62

63

Parameters:

64

- distinct_id: str - The distinct ID to associate with the current context and its children

65

66

Notes:

67

- Sets user ID for all subsequent operations in context

68

- Inherited by child contexts

69

- Used automatically by capture, set, and other operations

70

- Overrides any parent context user ID

71

"""

72

```

73

74

### Session Management

75

76

Manage user sessions within contexts for tracking related user activities across time.

77

78

```python { .api }

79

def set_context_session(session_id: str):

80

"""

81

Set the session ID for the current context.

82

83

Parameters:

84

- session_id: str - The session ID to associate with the current context and its children

85

86

Notes:

87

- Automatically included in all events as $session_id property

88

- Inherited by child contexts

89

- Used for session-based analytics and user journey tracking

90

"""

91

```

92

93

### Context Tagging

94

95

Add metadata and properties to contexts that are automatically included in all events and operations.

96

97

```python { .api }

98

def tag(name: str, value: Any):

99

"""

100

Add a tag to the current context.

101

102

Parameters:

103

- name: str - The tag key

104

- value: Any - The tag value (must be JSON serializable)

105

106

Notes:

107

- Tags are included in all subsequent events in the context

108

- Inherited by child contexts

109

- Useful for request IDs, feature flags, A/B test groups, etc.

110

- Automatically merged with event properties

111

"""

112

```

113

114

## Usage Examples

115

116

### Basic Context Usage

117

118

```python

119

import posthog

120

121

# Configure PostHog

122

posthog.api_key = 'phc_your_project_api_key'

123

124

# Simple context with user identification

125

with posthog.new_context():

126

posthog.identify_context('user123')

127

128

# All events automatically include user123

129

posthog.capture('page_viewed', {'page': 'dashboard'})

130

posthog.capture('button_clicked', {'button': 'export'})

131

posthog.set({'last_active': '2024-09-07'})

132

133

# Context with session tracking

134

with posthog.new_context():

135

posthog.identify_context('user456')

136

posthog.set_context_session('session_abc123')

137

138

# Events include both user ID and session ID

139

posthog.capture('session_started')

140

posthog.capture('feature_used', {'feature': 'reports'})

141

posthog.capture('session_ended')

142

```

143

144

### Context Tagging

145

146

```python

147

import posthog

148

149

# Context with multiple tags

150

with posthog.new_context():

151

posthog.identify_context('user789')

152

posthog.tag('request_id', 'req_12345')

153

posthog.tag('user_segment', 'premium')

154

posthog.tag('ab_test_group', 'variant_b')

155

posthog.tag('feature_flag_new_ui', True)

156

157

# All events automatically include these tags

158

posthog.capture('api_request', {

159

'endpoint': '/api/data',

160

'method': 'GET'

161

})

162

163

# Tags are merged with explicit properties

164

posthog.capture('error_occurred', {

165

'error_type': 'validation',

166

'error_code': 400

167

})

168

# Final event includes: user_id, request_id, user_segment, ab_test_group,

169

# feature_flag_new_ui, error_type, error_code

170

```

171

172

### Nested Contexts

173

174

```python

175

import posthog

176

177

# Parent context

178

with posthog.new_context():

179

posthog.identify_context('user123')

180

posthog.tag('request_type', 'api')

181

182

posthog.capture('request_started')

183

184

# Child context inherits parent data

185

with posthog.new_context():

186

posthog.tag('operation', 'data_processing')

187

188

posthog.capture('processing_started')

189

# Includes: user123, request_type=api, operation=data_processing

190

191

# Another child context

192

with posthog.new_context():

193

posthog.tag('step', 'validation')

194

195

posthog.capture('validation_completed')

196

# Includes: user123, request_type=api, operation=data_processing, step=validation

197

198

posthog.capture('request_completed')

199

# Back to parent context: user123, request_type=api

200

```

201

202

### Fresh Contexts

203

204

```python

205

import posthog

206

207

# Parent context with data

208

with posthog.new_context():

209

posthog.identify_context('user123')

210

posthog.tag('parent_tag', 'value')

211

212

# Fresh context ignores parent data

213

with posthog.new_context(fresh=True):

214

posthog.identify_context('admin456')

215

posthog.tag('admin_operation', True)

216

217

posthog.capture('admin_action')

218

# Only includes: admin456, admin_operation=True

219

# Does NOT include user123 or parent_tag

220

221

# Back to parent context

222

posthog.capture('user_action')

223

# Includes: user123, parent_tag=value

224

```

225

226

### Function-Level Contexts

227

228

```python

229

import posthog

230

231

# Decorator for automatic context management

232

@posthog.scoped()

233

def process_user_request(user_id, request_data):

234

posthog.identify_context(user_id)

235

posthog.tag('operation', 'user_request')

236

posthog.tag('request_id', request_data.get('id'))

237

238

posthog.capture('request_processing_started')

239

240

# Process request...

241

result = handle_request(request_data)

242

243

posthog.capture('request_processing_completed', {

244

'success': result['success'],

245

'processing_time': result['duration']

246

})

247

248

return result

249

250

# Background task with fresh context

251

@posthog.scoped(fresh=True)

252

def background_cleanup_task():

253

posthog.identify_context('system')

254

posthog.tag('task_type', 'cleanup')

255

256

posthog.capture('cleanup_started')

257

258

# Perform cleanup...

259

cleaned_items = perform_cleanup()

260

261

posthog.capture('cleanup_completed', {

262

'items_cleaned': len(cleaned_items)

263

})

264

265

# Usage

266

user_data = {'id': 'req_123', 'user': 'user456'}

267

process_user_request('user456', user_data)

268

269

# Run background task

270

background_cleanup_task()

271

```

272

273

### Exception Handling with Contexts

274

275

```python

276

import posthog

277

278

# Automatic exception capture (default behavior)

279

with posthog.new_context():

280

posthog.identify_context('user123')

281

posthog.tag('operation', 'risky_operation')

282

283

try:

284

posthog.capture('operation_started')

285

286

# This exception is automatically captured by context

287

raise ValueError("Something went wrong")

288

289

except ValueError as e:

290

# Exception was already captured by context

291

posthog.capture('operation_failed', {

292

'error_handled': True

293

})

294

295

# Disable automatic exception capture

296

with posthog.new_context(capture_exceptions=False):

297

posthog.identify_context('user456')

298

299

try:

300

dangerous_operation()

301

except Exception as e:

302

# Manual exception capture since auto-capture is disabled

303

posthog.capture_exception(e)

304

305

# Function-level exception handling

306

@posthog.scoped(capture_exceptions=True)

307

def risky_function():

308

posthog.identify_context('user789')

309

posthog.capture('function_started')

310

311

# Any exception here is automatically captured

312

raise RuntimeError("Function failed")

313

314

try:

315

risky_function()

316

except RuntimeError:

317

# Exception was already captured by the scoped decorator

318

pass

319

```

320

321

### Web Request Context Pattern

322

323

```python

324

import posthog

325

from flask import Flask, request, g

326

327

app = Flask(__name__)

328

329

@app.before_request

330

def before_request():

331

# Create context for each request

332

g.posthog_context = posthog.new_context()

333

g.posthog_context.__enter__()

334

335

# Set up request-level tracking

336

posthog.tag('request_id', request.headers.get('X-Request-ID', 'unknown'))

337

posthog.tag('user_agent', request.headers.get('User-Agent', 'unknown'))

338

posthog.tag('endpoint', request.endpoint)

339

340

# Identify user if available

341

if hasattr(g, 'current_user') and g.current_user:

342

posthog.identify_context(g.current_user.id)

343

344

@app.after_request

345

def after_request(response):

346

# Tag response information

347

posthog.tag('response_status', response.status_code)

348

349

# Capture request completion

350

posthog.capture('request_completed', {

351

'method': request.method,

352

'endpoint': request.endpoint,

353

'status_code': response.status_code

354

})

355

356

# Clean up context

357

if hasattr(g, 'posthog_context'):

358

g.posthog_context.__exit__(None, None, None)

359

360

return response

361

362

@app.route('/api/users/<user_id>')

363

def get_user(user_id):

364

posthog.capture('user_profile_viewed', {'viewed_user_id': user_id})

365

366

# Process request...

367

return {'user': user_id}

368

369

# Each request automatically gets its own context with request-level tags

370

```

371

372

### Async Context Management

373

374

```python

375

import posthog

376

import asyncio

377

378

async def async_operation():

379

# Contexts work with async operations

380

with posthog.new_context():

381

posthog.identify_context('async_user_123')

382

posthog.tag('operation_type', 'async')

383

384

posthog.capture('async_operation_started')

385

386

# Simulate async work

387

await asyncio.sleep(1)

388

389

posthog.capture('async_operation_completed')

390

391

# Run async operation

392

asyncio.run(async_operation())

393

394

# Async function decorator

395

@posthog.scoped()

396

async def async_task(task_data):

397

posthog.identify_context(task_data['user_id'])

398

posthog.tag('task_id', task_data['id'])

399

400

posthog.capture('async_task_started')

401

402

await process_task(task_data)

403

404

posthog.capture('async_task_completed')

405

406

# Usage

407

await async_task({'id': 'task_123', 'user_id': 'user_456'})

408

```

409

410

## Context Hierarchy and Inheritance

411

412

### Data Inheritance Rules

413

414

```python

415

import posthog

416

417

# Parent context

418

with posthog.new_context():

419

posthog.identify_context('parent_user')

420

posthog.tag('level', 'parent')

421

posthog.tag('shared', 'parent_value')

422

423

# Child context inherits parent data

424

with posthog.new_context():

425

posthog.tag('level', 'child') # Overrides parent tag

426

posthog.tag('child_only', 'child_value') # New tag

427

428

# Event includes:

429

# - user: parent_user (inherited)

430

# - level: child (overridden)

431

# - shared: parent_value (inherited)

432

# - child_only: child_value (new)

433

posthog.capture('child_event')

434

435

# Back to parent - child tags are gone

436

# Event includes:

437

# - user: parent_user

438

# - level: parent

439

# - shared: parent_value

440

posthog.capture('parent_event')

441

```

442

443

### Context Isolation

444

445

```python

446

import posthog

447

448

# Sibling contexts are isolated

449

with posthog.new_context():

450

posthog.identify_context('parent_user')

451

452

# First child

453

with posthog.new_context():

454

posthog.tag('branch', 'first')

455

posthog.capture('first_child_event')

456

457

# Second child (no access to first child's data)

458

with posthog.new_context():

459

posthog.tag('branch', 'second')

460

posthog.capture('second_child_event') # Does NOT include branch=first

461

```

462

463

## Best Practices

464

465

### Context Scope Management

466

467

```python

468

# Good - Clear context boundaries

469

with posthog.new_context():

470

posthog.identify_context('user123')

471

process_user_request()

472

473

# Good - Function-level contexts for discrete operations

474

@posthog.scoped()

475

def handle_api_endpoint():

476

posthog.identify_context(get_current_user())

477

# Process request

478

479

# Avoid - Contexts that span too long

480

# with posthog.new_context(): # Don't do this

481

# for user in all_users: # This context lasts too long

482

# process_user(user)

483

```

484

485

### Tag Naming and Organization

486

487

```python

488

# Good - Consistent, descriptive tag names

489

with posthog.new_context():

490

posthog.tag('request_id', 'req_123')

491

posthog.tag('user_segment', 'premium')

492

posthog.tag('feature_flag_new_ui', True)

493

posthog.tag('ab_test_variant', 'control')

494

495

# Avoid - Inconsistent or unclear tag names

496

with posthog.new_context():

497

posthog.tag('reqId', 'req_123') # Inconsistent naming

498

posthog.tag('flag1', True) # Unclear purpose

499

posthog.tag('test', 'control') # Too generic

500

```

501

502

### Exception Handling Strategy

503

504

```python

505

# Enable exception capture for user-facing operations

506

@posthog.scoped(capture_exceptions=True)

507

def user_request_handler():

508

# Exceptions automatically captured

509

pass

510

511

# Disable for internal/system operations where exceptions are expected

512

@posthog.scoped(capture_exceptions=False)

513

def system_health_check():

514

# Handle exceptions manually

515

try:

516

check_system()

517

except ExpectedError as e:

518

# Don't capture expected errors

519

pass

520

except UnexpectedError as e:

521

posthog.capture_exception(e)

522

```

523

524

### Thread Safety

525

526

PostHog contexts are thread-safe and work correctly in multi-threaded applications:

527

528

```python

529

import posthog

530

import threading

531

532

def worker_thread(worker_id):

533

# Each thread gets its own context

534

with posthog.new_context():

535

posthog.identify_context(f'worker_{worker_id}')

536

posthog.tag('thread_id', threading.current_thread().ident)

537

538

posthog.capture('worker_started')

539

# Do work...

540

posthog.capture('worker_completed')

541

542

# Start multiple worker threads

543

threads = []

544

for i in range(5):

545

t = threading.Thread(target=worker_thread, args=(i,))

546

threads.append(t)

547

t.start()

548

549

for t in threads:

550

t.join()

551

```