5
5
This module handles the creation and management of Chrome WebDriver instances.
6
6
"""
7
7
8
+ import logging
8
9
import os
9
10
import sys
10
11
from typing import Dict , Optional
34
35
# Global driver storage to reuse sessions
35
36
active_drivers : Dict [str , webdriver .Chrome ] = {}
36
37
38
+ logger = logging .getLogger (__name__ )
39
+
37
40
38
41
def get_or_create_driver () -> Optional [webdriver .Chrome ]:
39
42
"""
@@ -55,8 +58,8 @@ def get_or_create_driver() -> Optional[webdriver.Chrome]:
55
58
56
59
# Set up Chrome options
57
60
chrome_options = Options ()
58
- print (
59
- f"🌐 Running browser in { 'headless' if config .chrome .headless else 'visible' } mode"
61
+ logger . info (
62
+ f"Running browser in { 'headless' if config .chrome .headless else 'visible' } mode"
60
63
)
61
64
if config .chrome .headless :
62
65
chrome_options .add_argument ("--headless=new" )
@@ -78,22 +81,22 @@ def get_or_create_driver() -> Optional[webdriver.Chrome]:
78
81
79
82
# Initialize Chrome driver
80
83
try :
81
- print ( "🌐 Initializing Chrome WebDriver..." )
84
+ logger . info ( " Initializing Chrome WebDriver..." )
82
85
83
86
# Use ChromeDriver path from environment or config
84
87
chromedriver_path = (
85
88
os .environ .get ("CHROMEDRIVER_PATH" ) or config .chrome .chromedriver_path
86
89
)
87
90
88
91
if chromedriver_path :
89
- print (f"🌐 Using ChromeDriver at path: { chromedriver_path } " )
92
+ logger . info (f"Using ChromeDriver at path: { chromedriver_path } " )
90
93
service = Service (executable_path = chromedriver_path )
91
94
driver = webdriver .Chrome (service = service , options = chrome_options )
92
95
else :
93
- print ( "🌐 Using auto-detected ChromeDriver" )
96
+ logger . info ( " Using auto-detected ChromeDriver" )
94
97
driver = webdriver .Chrome (options = chrome_options )
95
98
96
- print ( "✅ Chrome WebDriver initialized successfully" )
99
+ logger . info ( " Chrome WebDriver initialized successfully" )
97
100
98
101
# Add a page load timeout for safety
99
102
driver .set_page_load_timeout (60 )
@@ -103,7 +106,7 @@ def get_or_create_driver() -> Optional[webdriver.Chrome]:
103
106
for attempt in range (max_retries ):
104
107
try :
105
108
if login_to_linkedin (driver ):
106
- print ("Successfully logged in to LinkedIn" )
109
+ logger . info ("Successfully logged in to LinkedIn" )
107
110
active_drivers [session_id ] = driver
108
111
return driver
109
112
except (
@@ -123,15 +126,18 @@ def get_or_create_driver() -> Optional[webdriver.Chrome]:
123
126
# In interactive mode, handle the error and potentially retry
124
127
should_retry = handle_login_error (e )
125
128
if should_retry and attempt < max_retries - 1 :
126
- print (f"🔄 Retry attempt { attempt + 2 } /{ max_retries } " )
129
+ logger . info (f"Retry attempt { attempt + 2 } /{ max_retries } " )
127
130
continue
128
131
else :
129
132
# Clean up driver on final failure
130
133
driver .quit ()
131
134
return None
132
135
except Exception as e :
133
- error_msg = f"🛑 Error creating web driver: { e } "
134
- print (error_msg )
136
+ error_msg = f"Error creating web driver: { e } "
137
+ logger .error (
138
+ error_msg ,
139
+ extra = {"exception_type" : type (e ).__name__ , "exception_message" : str (e )},
140
+ )
135
141
136
142
if config .chrome .non_interactive :
137
143
raise DriverInitializationError (error_msg )
@@ -169,7 +175,7 @@ def login_to_linkedin(driver: webdriver.Chrome) -> bool:
169
175
raise CredentialsNotFoundError ("No credentials available" )
170
176
171
177
# Login to LinkedIn using enhanced linkedin-scraper
172
- print ( "🔑 Logging in to LinkedIn..." )
178
+ logger . info ( " Logging in to LinkedIn..." )
173
179
174
180
from linkedin_scraper import actions # type: ignore
175
181
@@ -182,7 +188,7 @@ def login_to_linkedin(driver: webdriver.Chrome) -> bool:
182
188
interactive = not config .chrome .non_interactive ,
183
189
)
184
190
185
- print ( "✅ Successfully logged in to LinkedIn" )
191
+ logger . info ( " Successfully logged in to LinkedIn" )
186
192
return True
187
193
188
194
except Exception :
@@ -203,7 +209,7 @@ def login_to_linkedin(driver: webdriver.Chrome) -> bool:
203
209
204
210
elif "feed" in current_url or "mynetwork" in current_url :
205
211
# Actually logged in successfully despite the exception
206
- print ( "✅ Successfully logged in to LinkedIn" )
212
+ logger . info ( " Successfully logged in to LinkedIn" )
207
213
return True
208
214
209
215
else :
@@ -232,10 +238,10 @@ def handle_login_error(error: Exception) -> bool:
232
238
"""
233
239
config = get_config ()
234
240
235
- print (f"\n ❌ { str (error )} " )
241
+ logger . error (f"\n ❌ { str (error )} " )
236
242
237
243
if config .chrome .headless :
238
- print (
244
+ logger . info (
239
245
"🔍 Try running with visible browser window: uv run main.py --no-headless"
240
246
)
241
247
@@ -252,8 +258,8 @@ def handle_login_error(error: Exception) -> bool:
252
258
)
253
259
if retry and retry .get ("retry" , False ):
254
260
clear_credentials_from_keyring ()
255
- print ("✅ Credentials cleared from keyring." )
256
- print ("🔄 Retrying with new credentials..." )
261
+ logger . info ("✅ Credentials cleared from keyring." )
262
+ logger . info ("🔄 Retrying with new credentials..." )
257
263
return True
258
264
259
265
return False
@@ -266,31 +272,33 @@ def initialize_driver() -> None:
266
272
config = get_config ()
267
273
268
274
if config .server .lazy_init :
269
- print ("Using lazy initialization - driver will be created on first tool call" )
275
+ logger .info (
276
+ "Using lazy initialization - driver will be created on first tool call"
277
+ )
270
278
if config .linkedin .email and config .linkedin .password :
271
- print ("LinkedIn credentials found in configuration" )
279
+ logger . info ("LinkedIn credentials found in configuration" )
272
280
else :
273
- print (
281
+ logger . info (
274
282
"No LinkedIn credentials found - will look for stored credentials on first use"
275
283
)
276
284
return
277
285
278
286
# Validate chromedriver can be found
279
287
if config .chrome .chromedriver_path :
280
- print (f"✅ ChromeDriver found at: { config .chrome .chromedriver_path } " )
288
+ logger . info (f"✅ ChromeDriver found at: { config .chrome .chromedriver_path } " )
281
289
os .environ ["CHROMEDRIVER" ] = config .chrome .chromedriver_path
282
290
else :
283
- print ("⚠️ ChromeDriver not found in common locations." )
284
- print ("⚡ Continuing with automatic detection..." )
285
- print (
291
+ logger . info ("⚠️ ChromeDriver not found in common locations." )
292
+ logger . info ("⚡ Continuing with automatic detection..." )
293
+ logger . info (
286
294
"💡 Tip: install ChromeDriver and set the CHROMEDRIVER environment variable"
287
295
)
288
296
289
297
# Create driver and log in
290
298
try :
291
299
driver = get_or_create_driver ()
292
300
if driver :
293
- print ("✅ Web driver initialized successfully" )
301
+ logger . info ("✅ Web driver initialized successfully" )
294
302
else :
295
303
# Driver creation failed - always raise an error
296
304
raise DriverInitializationError ("Failed to initialize web driver" )
@@ -310,7 +318,7 @@ def initialize_driver() -> None:
310
318
raise DriverInitializationError (
311
319
f"Failed to initialize web driver: { str (e )} "
312
320
)
313
- print (f"❌ Failed to initialize web driver: { str (e )} " )
321
+ logger . error (f"❌ Failed to initialize web driver: { str (e )} " )
314
322
handle_driver_error ()
315
323
316
324
@@ -322,7 +330,9 @@ def handle_driver_error() -> None:
322
330
323
331
# Skip interactive handling in non-interactive mode
324
332
if config .chrome .non_interactive :
325
- print ("❌ ChromeDriver is required for this application to work properly." )
333
+ logger .error (
334
+ "❌ ChromeDriver is required for this application to work properly."
335
+ )
326
336
sys .exit (1 )
327
337
328
338
questions = [
@@ -347,24 +357,28 @@ def handle_driver_error() -> None:
347
357
# Update config with the new path
348
358
config .chrome .chromedriver_path = path
349
359
os .environ ["CHROMEDRIVER" ] = path
350
- print (f"✅ ChromeDriver path set to: { path } " )
351
- print ("💡 Please restart the application to use the new ChromeDriver path." )
352
- print (" Example: uv run main.py" )
360
+ logger .info (f"✅ ChromeDriver path set to: { path } " )
361
+ logger .info (
362
+ "💡 Please restart the application to use the new ChromeDriver path."
363
+ )
364
+ logger .info (" Example: uv run main.py" )
353
365
sys .exit (0 )
354
366
else :
355
- print (f"⚠️ Warning: The specified path does not exist: { path } " )
356
- print ("💡 Please check the path and restart the application." )
367
+ logger . warning (f"⚠️ Warning: The specified path does not exist: { path } " )
368
+ logger . info ("💡 Please check the path and restart the application." )
357
369
sys .exit (1 )
358
370
359
371
elif answers ["chromedriver_action" ] == "help" :
360
- print ("\n 📋 ChromeDriver Installation Guide:" )
361
- print ("1. Find your Chrome version: Chrome menu > Help > About Google Chrome" )
362
- print (
372
+ logger .info ("\n 📋 ChromeDriver Installation Guide:" )
373
+ logger .info (
374
+ "1. Find your Chrome version: Chrome menu > Help > About Google Chrome"
375
+ )
376
+ logger .info (
363
377
"2. Download matching ChromeDriver: https://chromedriver.chromium.org/downloads"
364
378
)
365
- print ("3. Place ChromeDriver in a location on your PATH" )
366
- print (" - macOS/Linux: /usr/local/bin/ is recommended" )
367
- print (
379
+ logger . info ("3. Place ChromeDriver in a location on your PATH" )
380
+ logger . info (" - macOS/Linux: /usr/local/bin/ is recommended" )
381
+ logger . info (
368
382
" - Windows: Add to a directory in your PATH or specify the full path\n "
369
383
)
370
384
@@ -373,5 +387,5 @@ def handle_driver_error() -> None:
373
387
)["try_again" ]:
374
388
initialize_driver ()
375
389
376
- print ("❌ ChromeDriver is required for this application to work properly." )
390
+ logger . error ("❌ ChromeDriver is required for this application to work properly." )
377
391
sys .exit (1 )
0 commit comments