
    i"4                     4   d Z ddlZddlZddlZddlmZmZ ddlmZ ddlm	Z	m
Z
 ddlmZmZ ddd	d
Z ej        d          Z ej        d          Z ej        d          ZddddddZdede	e         fdZdede	e         fdZdede	e         fdZdede	e         fdZdede
e         defdZddddddede	e         de	e         de	e         d e	e
e                  defd!Z	 d5d#d$ddddd%d&ed'ed(ed)e	e         d*e	e         de	e         d+e	e         ddfd,Zd$ddddd-d.ed'ed/ede	e         de	e         de	e         d e	e
e                  defd0Zd.ed1edefd2Z dddddd.ede	e         de	e         de	e         d e	e
e                  ddfd3Z!d6d4Z"dS )7u(  ``hermes logs`` — view and filter Hermes log files.

Supports tailing, following, session filtering, level filtering,
component filtering, and relative time ranges.  All log files live
under ``~/.hermes/logs/``.

Usage examples::

    hermes logs                    # last 50 lines of agent.log
    hermes logs -f                 # follow agent.log in real time
    hermes logs errors             # last 50 lines of errors.log
    hermes logs gateway -n 100    # last 100 lines of gateway.log
    hermes logs --level WARNING    # only WARNING+ lines
    hermes logs --session abc123   # filter by session ID substring
    hermes logs --component tools  # only tool-related lines
    hermes logs --since 1h         # lines from the last hour
    hermes logs --since 30m -f     # follow, starting 30 min ago
    N)datetime	timedelta)Path)OptionalSequence)get_hermes_homedisplay_hermes_homez	agent.logz
errors.logzgateway.log)agenterrorsgatewayz(^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})z'\s(DEBUG|INFO|WARNING|ERROR|CRITICAL)\sz?\s(?:DEBUG|INFO|WARNING|ERROR|CRITICAL)(?:\s+\[.*?\])?\s+(\S+):            )DEBUGINFOWARNINGERRORCRITICAL	since_strreturnc                    |                                                                  } t          j        d|           }|sdS t	          |                    d                    }|                    d          }t          |          t          |          t          |          t          |          d	|         }t          j                    |z
  S )
zParse a relative time string like '1h', '30m', '2d' into a datetime cutoff.

    Returns None if the string can't be parsed.
    z^(\d+)\s*([smhd])$Nr   r   )seconds)minutes)hours)days)smhd)	striplowerrematchintgroupr   r   now)r   r$   valueunitdeltas        4/home/ubuntu/.hermes/hermes-agent/hermes_cli/logs.py_parse_sincer,   8   s    
 !!''))IH*I66E tAE;;q>>Du%%%u%%%U###E"""	 
 E <>>E!!    linec                     t                               |           }|sdS 	 t          j        |                    d          d          S # t
          $ r Y dS w xY w)zAExtract timestamp from a log line. Returns None if not parseable.Nr   z%Y-%m-%d %H:%M:%S)_TS_REr$   r   strptimer&   
ValueErrorr.   r   s     r+   _parse_line_timestampr4   L   se    TA t -@AAA   tts   'A 
AAc                 h    t                               |           }|r|                    d          ndS )z"Extract the log level from a line.r   N)	_LEVEL_REsearchr&   r3   s     r+   _extract_levelr8   W   s.    A$1771:::$r-   c                 h    t                               |           }|r|                    d          ndS )z(Extract the logger name from a log line.r   N)_LOGGER_NAME_REr7   r&   r3   s     r+   _extract_logger_namer;   ]   s.    t$$A$1771:::$r-   prefixesc                 l    t          |           }|dS |                    t          |                    S )z@Check if a log line's logger name starts with any of *prefixes*.NF)r;   
startswithtuple)r.   r<   names      r+   _line_matches_componentrA   c   s1    %%D|u??5??+++r-   	min_levelsession_filtersincecomponent_prefixesrC   rD   rE   rF   c                   |t          |           }|||k     rdS |Kt          |           }|:t                              |d          t                              |d          k     rdS ||| vrdS |t	          | |          sdS dS )z.Check if a log line passes all active filters.NFr   T)r4   r8   _LEVEL_ORDERgetrA   )r.   rC   rD   rE   rF   tslevels          r+   _matches_filtersrL   k   s     "4((>b5jj5t$$q))L,<,<Y,J,JJJu!%%5%&t-?@@ 	54r-   r
   2   F)	num_linesfollowrK   sessionrE   	componentlog_namerN   rO   rK   rP   rQ   c          
         t                               |           }|Nt          d| dd                    t	          t                                           t          j        d           t                      dz  |z  }|                                s5t          d|            t          d           t          j        d           d}	|r8t          |          }	|	't          d	|d
           t          j        d           |r|
                                nd}
|
r0|
t          vr't          d|d           t          j        d           d}|rqddlm} |                                }||vrKd                    t	          |                    }t          d|d|            t          j        d           ||         }|
dup|dup|	dup|du}	 t          ||||
||	|          }n6# t           $ r) t          d|            t          j        d           Y nw xY wg }|
r|                    d|
            |r|                    d|            |r|                    d|            |r|                    d|            |rdd                    |           dnd}|r%t          dt%                       d| | d           n't          dt%                       d| | d| d           |D ]}t          |d           |sdS 	 t'          ||
||	|           dS # t(          $ r t          d            Y dS w xY w)!a\  Read and display log lines, optionally following in real time.

    Parameters
    ----------
    log_name
        Which log to read: ``"agent"``, ``"errors"``, ``"gateway"``.
    num_lines
        Number of recent lines to show (before follow starts).
    follow
        If True, keep watching for new lines (Ctrl+C to stop).
    level
        Minimum log level to show (e.g. ``"WARNING"``).
    session
        Session ID substring to filter on.
    since
        Relative time string (e.g. ``"1h"``, ``"30m"``).
    component
        Component name to filter by (e.g. ``"gateway"``, ``"tools"``).
    NzUnknown log: z. Available: z, r   logszLog file not found: u?   (Logs are created when Hermes runs — try 'hermes chat' first)zInvalid --since value: z$. Use format like '1h', '30m', '2d'.zInvalid --level: z/. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL.r   )COMPONENT_PREFIXESzUnknown component: has_filtersrC   rD   rE   rF   zPermission denied: zlevel>=zsession=z
component=zsince=z [] z--- /logs/z (Ctrl+C to stop) ---z (last z) ---endrB   z
--- stopped ---)	LOG_FILESrI   printjoinsortedsysexitr   existsr,   upperrH   hermes_loggingrU   r"   
_read_tailPermissionErrorappendr	   _follow_logKeyboardInterrupt)rR   rN   rO   rK   rP   rE   rQ   filenamelog_pathsince_dtrC   rF   rU   component_lower	availablerW   linesfilter_partsfilter_descr.   s                       r+   tail_logrs      sX   : }}X&&HUhUUtyy	ARAR7S7SUUVVV  6)H4H?? /X//000PQQQ H &&YEYYYZZZHQKKK!&0DI Yl22Z%ZZZ[[[  A555555#//++"444		&);"<"<==IM	MM)MMNNNHQKKK/@ 	 	*$	*4	* T)	 8YK%.w!)>PR R R    .H..///
 L 31i11222 20w00111 6444555 .,U,,---5AI1tyy..1111rK b^(**^^(^K^^^____`(**``(`K``PY```aaa  d #H	'#8J	L 	L 	L 	L 	L 	L # # #!""""""#s$   G+ +0HHL' 'MMrV   pathrW   c                    |r=t          | t          |dz  d                    }fd|D             }|| d         S t          | |          S )zRead the last *num_lines* matching lines from a log file.

    When filters are active, we read more raw lines to find enough matches.
       i  c           	      <    g | ]}t          |           |S )rB   )rL   ).0lrF   rC   rD   rE   s     r+   
<listcomp>z_read_tail.<locals>.<listcomp>  sM     
 
 
Y/=U3EG G G

 
 
r-   N)_read_last_n_linesmax)	rt   rN   rW   rC   rD   rE   rF   	raw_linesfiltereds	      ````  r+   rf   rf      s      3 'tSR-F-FGG	
 
 
 
 
 
 
 
 
 
 
$$!$	222r-   nc                    	 |                                  j        }|dk    rg S |dk    rJt          | ddd          5 }|                                }ddd           n# 1 swxY w Y   || d         S t          | d          5 }d	}g }|}|dk    rt	          |          |d
z   k    rt          ||          }||z  }|                    |           |                    |          }	|	                    d          }
|r"|
d         |d         z   |d<   |
dd         |z   }n|
}t          |dz  d          }|dk    rt	          |          |d
z   k    g }|D ]}|	                                s	 |
                    |                    dd          dz              F# t          $ r. |
                    |                    d          dz              Y }w xY w|| d         cddd           S # 1 swxY w Y   dS # t          $ rM t          | ddd          5 }|                                }ddd           n# 1 swxY w Y   || d         cY S w xY w)zEfficiently read the last N lines from a file.

    For files under 1MB, reads the whole file (fast, simple).
    For larger files, reads chunks from the end.
    r      rutf-8replaceencodingr   Nrbi    r      
r   i   )r   
zlatin-1)statst_sizeopen	readlineslenminseekreadsplitr!   rh   decode	Exception)rt   r   sizef	all_lines
chunk_sizerp   pos	read_sizechunkchunk_linesdecodedraws                r+   r{   r{     sf   /yy{{"199I 9dC')DDD *KKMM	* * * * * * * * * * * * * * *aRSS>! $ 	 JEC''c%jjAE11
C00	y sy))#kk%00 (  +2q9E!H',u4EE'E a77
 ''c%jjAE11  G A Ayy{{ ANN3::gi:#H#H4#OPPPP  A A ANN3::i#8#84#?@@@@@AA233<;	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 >    $gi@@@ 	&AI	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	&!~	s    G8 G8 AG8 A!!G8 $A!%G8 3G8 C'G++-FG+5GG+GG+G8 +G//G8 2G/3G8 8IH6*I6H:	:I=H:	>IIc                P   t          | ddd          5 }|                    dd           	 |                                }|rDt          |||||          r/t	          |d	
           t
          j                                         nt          j	        d           o# 1 swxY w Y   dS )z9Poll a log file for new content and print matching lines.r   r   r   r   r   r   TrB   rY   r[   g333333?N)
r   r   readlinerL   r^   ra   stdoutflushtimesleep)rt   rC   rD   rE   rF   r   r.   s          r+   ri   ri   N  s     
dC')	<	<	<  	q!		 ::<<D  #DI3A7IK K K ' $B''''J$$&&&
3		                    s   BBB"Bc            	      (   t                      dz  } |                                 s!t          dt                       d           dS t          dt                       d           d}t	          |                                           D ]t}|                                r\|j        dk    rP|                                j	        }t          j        |                                j                  }|d	k     r| d
}n|dk     r
|d	z  dd}n	|dz  dd}t          j                    |z
  }|                                dk     rd}n|                                dk     r(t          |                                dz             d}nU|                                dk     r(t          |                                dz             d}n|                    d          }t          d|j        dd|dd|            d}v|st          d           dS dS )z%Print available log files with sizes.rT   zNo logs directory at rZ   NzLog files in z/logs/:
Fz.logi   Br   z.1fKBMB<   zjust nowi  zm agoiQ zh agoz%Y-%m-%dz  z<25 z>8z   Tu;     (no log files yet — run 'hermes chat' to generate logs))r   rc   r^   r	   r`   iterdiris_filesuffixr   r   r   fromtimestampst_mtimer'   total_secondsr%   strftimer@   )log_dirfoundentryr   mtimesize_strageage_strs           r+   	list_logsr   f  sD   &(G>> C&9&;&;CCCDDD	
:-//
:
:
:;;;E))**  ==?? 	u|v55::<<'D*5::<<+@AAEd{{":::##"Tk1111"k2::::,..5(C  ""R''$""$$t++ !2!2!4!4r!9::AAA""$$u,, !2!2!4!4t!;<<CCC..44AuzAAAAAAAABBBE MKLLLLLM Mr-   )r
   )r   N)#__doc__r#   ra   r   r   r   pathlibr   typingr   r   hermes_constantsr   r	   r]   compiler0   r6   r:   rH   strr,   r4   r8   r;   boolrA   rL   r%   rs   listrf   r{   ri   r    r-   r+   <module>r      s=   & 
			 



  ( ( ( ( ( ( ( (       % % % % % % % % A A A A A A A A  	 
?	@	@ BJABB	
 "*  A!aQOO"C "HX$6 " " " "( (:    % %# % % % %%s %x} % % % %,# ,# ,4 , , , ,  $$( $26  
 } SM	
 H !#/ 
   @ l# !#l# l# l#l# l# 	l#
 C=l# c]l# C=l# }l# 
l# l# l# l#f #$( $263 3 3
33 	3
 }3 SM3 H3 !#/3 
3 3 3 3:5T 5c 5d 5 5 5 5v  $$( $26     
  }  SM	 
 H  !#/  
       0 M  M  M  M  M  Mr-   