Compare commits

..

2 commits

Author SHA1 Message Date
6091183f29
add more code & doc 2023-02-10 14:01:21 +01:00
cb8ef9817a
Introduce AddonLoader 2023-02-10 14:01:20 +01:00
747 changed files with 7435 additions and 69161 deletions

View file

@ -6,24 +6,5 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
trim_trailing_whitespaces = true
indent_style = tab indent_style = tab
trim_trailing_whitespace = true
insert_final_newline = true
quote_type = single
max_line_length = off
[*.js]
quote_type = double
ij_javascript_use_double_quotes = true
[*.yml]
indent_style = space
indent_size = 2
[*.xml]
indent_style = space
indent_size = 2
[*.json]
indent_style = space
indent_size = 2

View file

@ -1,397 +1,397 @@
[main] [main]
host = https://api.transifex.com host = https://www.transifex.com
[o:Friendica:p:friendica:r:addon_advancedcontentfilter_messagespo] [friendica.addon_advancedcontentfilter_messagespo]
file_filter = advancedcontentfilter/lang/<lang>/messages.po file_filter = advancedcontentfilter/lang/<lang>/messages.po
source_file = advancedcontentfilter/lang/C/messages.po source_file = advancedcontentfilter/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_blackout_messagespo] [friendica.addon_blackout_messagespo]
file_filter = blackout/lang/<lang>/messages.po file_filter = blackout/lang/<lang>/messages.po
source_file = blackout/lang/C/messages.po source_file = blackout/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_blockem_messagespo] [friendica.addon_blockem_messagespo]
file_filter = blockem/lang/<lang>/messages.po file_filter = blockem/lang/<lang>/messages.po
source_file = blockem/lang/C/messages.po source_file = blockem/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_blogger_messagespo] [friendica.addon_blogger_messagespo]
file_filter = blogger/lang/<lang>/messages.po file_filter = blogger/lang/<lang>/messages.po
source_file = blogger/lang/C/messages.po source_file = blogger/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_buffer_messagespo] [friendica.addon_buffer_messagespo]
file_filter = buffer/lang/<lang>/messages.po file_filter = buffer/lang/<lang>/messages.po
source_file = buffer/lang/C/messages.po source_file = buffer/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_buglink_messagespo] [friendica.addon_buglink_messagespo]
file_filter = buglink/lang/<lang>/messages.po file_filter = buglink/lang/<lang>/messages.po
source_file = buglink/lang/C/messages.po source_file = buglink/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_catavatar_messagespo] [friendica.addon_catavatar_messagespo]
file_filter = catavatar/lang/<lang>/messages.po file_filter = catavatar/lang/<lang>/messages.po
source_file = catavatar/lang/C/messages.po source_file = catavatar/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_cookienotice_messagespo] [friendica.addon_cookienotice_messagespo]
file_filter = cookienotice/lang/<lang>/messages.po file_filter = cookienotice/lang/<lang>/messages.po
source_file = cookienotice/lang/C/messages.po source_file = cookienotice/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_curweather_messagespo] [friendica.addon_curweather_messagespo]
file_filter = curweather/lang/<lang>/messages.po file_filter = curweather/lang/<lang>/messages.po
source_file = curweather/lang/C/messages.po source_file = curweather/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_diaspora_messagespo] [friendica.addon_diaspora_messagespo]
file_filter = diaspora/lang/<lang>/messages.po file_filter = diaspora/lang/<lang>/messages.po
source_file = diaspora/lang/C/messages.po source_file = diaspora/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_dwpost_messagespo] [friendica.addon_dwpost_messagespo]
file_filter = dwpost/lang/<lang>/messages.po file_filter = dwpost/lang/<lang>/messages.po
source_file = dwpost/lang/C/messages.po source_file = dwpost/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_forumdirectory_messagespo] [friendica.addon_forumdirectory_messagespo]
file_filter = forumdirectory/lang/<lang>/messages.po file_filter = forumdirectory/lang/<lang>/messages.po
source_file = forumdirectory/lang/C/messages.po source_file = forumdirectory/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_fromapp_messagespo] [friendica.addon_fromapp_messagespo]
file_filter = fromapp/lang/<lang>/messages.po file_filter = fromapp/lang/<lang>/messages.po
source_file = fromapp/lang/C/messages.po source_file = fromapp/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_fromgplus_messagespo] [friendica.addon_fromgplus_messagespo]
file_filter = fromgplus/lang/<lang>/messages.po file_filter = fromgplus/lang/<lang>/messages.po
source_file = fromgplus/lang/C/messages.po source_file = fromgplus/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_geonames_messagespo] [friendica.addon_geonames_messagespo]
file_filter = geonames/lang/<lang>/messages.po file_filter = geonames/lang/<lang>/messages.po
source_file = geonames/lang/C/messages.po source_file = geonames/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_gnot_messagespo] [friendica.addon_gnot_messagespo]
file_filter = gnot/lang/<lang>/messages.po file_filter = gnot/lang/<lang>/messages.po
source_file = gnot/lang/C/messages.po source_file = gnot/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_gravatar_messagespo] [friendica.addon_gravatar_messagespo]
file_filter = gravatar/lang/<lang>/messages.po file_filter = gravatar/lang/<lang>/messages.po
source_file = gravatar/lang/C/messages.po source_file = gravatar/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_group_text_messagespo] [friendica.addon_group_text_messagespo]
file_filter = group_text/lang/<lang>/messages.po file_filter = group_text/lang/<lang>/messages.po
source_file = group_text/lang/C/messages.po source_file = group_text/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_ifttt_messagespo] [friendica.addon_ifttt_messagespo]
file_filter = ifttt/lang/<lang>/messages.po file_filter = ifttt/lang/<lang>/messages.po
source_file = ifttt/lang/C/messages.po source_file = ifttt/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_ijpost_messagespo] [friendica.addon_ijpost_messagespo]
file_filter = ijpost/lang/<lang>/messages.po file_filter = ijpost/lang/<lang>/messages.po
source_file = ijpost/lang/C/messages.po source_file = ijpost/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_impressum_messagespo] [friendica.addon_impressum_messagespo]
file_filter = impressum/lang/<lang>/messages.po file_filter = impressum/lang/<lang>/messages.po
source_file = impressum/lang/C/messages.po source_file = impressum/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_infiniteimprobabilitydrive_messagespo] [friendica.addon_infiniteimprobabilitydrive_messagespo]
file_filter = infiniteimprobabilitydrive/lang/<lang>/messages.po file_filter = infiniteimprobabilitydrive/lang/<lang>/messages.po
source_file = infiniteimprobabilitydrive/lang/C/messages.po source_file = infiniteimprobabilitydrive/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_irc_messagespo] [friendica.addon_irc_messagespo]
file_filter = irc/lang/<lang>/messages.po file_filter = irc/lang/<lang>/messages.po
source_file = irc/lang/C/messages.po source_file = irc/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_jappixmini_messagespo] [friendica.addon_jappixmini_messagespo]
file_filter = jappixmini/lang/<lang>/messages.po file_filter = jappixmini/lang/<lang>/messages.po
source_file = jappixmini/lang/C/messages.po source_file = jappixmini/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_js_upload_messagespo] [friendica.addon_js_upload_messagespo]
file_filter = js_upload/lang/<lang>/messages.po file_filter = js_upload/lang/<lang>/messages.po
source_file = js_upload/lang/C/messages.po source_file = js_upload/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_krynn_messagespo] [friendica.addon_krynn_messagespo]
file_filter = krynn/lang/<lang>/messages.po file_filter = krynn/lang/<lang>/messages.po
source_file = krynn/lang/C/messages.po source_file = krynn/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_langfilter_messagespo] [friendica.addon_langfilter_messagespo]
file_filter = langfilter/lang/<lang>/messages.po file_filter = langfilter/lang/<lang>/messages.po
source_file = langfilter/lang/C/messages.po source_file = langfilter/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_libertree_messagespo] [friendica.addon_libertree_messagespo]
file_filter = libertree/lang/<lang>/messages.po file_filter = libertree/lang/<lang>/messages.po
source_file = libertree/lang/C/messages.po source_file = libertree/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_libravatar_messagespo] [friendica.addon_libravatar_messagespo]
file_filter = libravatar/lang/<lang>/messages.po file_filter = libravatar/lang/<lang>/messages.po
source_file = libravatar/lang/C/messages.po source_file = libravatar/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_ljpost_messagespo] [friendica.addon_ljpost_messagespo]
file_filter = ljpost/lang/<lang>/messages.po file_filter = ljpost/lang/<lang>/messages.po
source_file = ljpost/lang/C/messages.po source_file = ljpost/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_mailstream_messagespo] [friendica.addon_mailstream_messagespo]
file_filter = mailstream/lang/<lang>/messages.po file_filter = mailstream/lang/<lang>/messages.po
source_file = mailstream/lang/C/messages.po source_file = mailstream/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_mathjax_messagespo] [friendica.addon_mathjax_messagespo]
file_filter = mathjax/lang/<lang>/messages.po file_filter = mathjax/lang/<lang>/messages.po
source_file = mathjax/lang/C/messages.po source_file = mathjax/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_membersince_messagespo] [friendica.addon_membersince_messagespo]
file_filter = membersince/lang/<lang>/messages.po file_filter = membersince/lang/<lang>/messages.po
source_file = membersince/lang/C/messages.po source_file = membersince/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_morechoice_messagespo] [friendica.addon_morechoice_messagespo]
file_filter = morechoice/lang/<lang>/messages.po file_filter = morechoice/lang/<lang>/messages.po
source_file = morechoice/lang/C/messages.po source_file = morechoice/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_morepokes_messagespo] [friendica.addon_morepokes_messagespo]
file_filter = morepokes/lang/<lang>/messages.po file_filter = morepokes/lang/<lang>/messages.po
source_file = morepokes/lang/C/messages.po source_file = morepokes/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_newmemberwidget_messagespo] [friendica.addon_newmemberwidget_messagespo]
file_filter = newmemberwidget/lang/<lang>/messages.po file_filter = newmemberwidget/lang/<lang>/messages.po
source_file = newmemberwidget/lang/C/messages.po source_file = newmemberwidget/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_notifyall_messagespo] [friendica.addon_notifyall_messagespo]
file_filter = notifyall/lang/<lang>/messages.po file_filter = notifyall/lang/<lang>/messages.po
source_file = notifyall/lang/C/messages.po source_file = notifyall/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_notimeline_messagespo] [friendica.addon_notimeline_messagespo]
file_filter = notimeline/lang/<lang>/messages.po file_filter = notimeline/lang/<lang>/messages.po
source_file = notimeline/lang/C/messages.po source_file = notimeline/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_nsfw_messagespo] [friendica.addon_nsfw_messagespo]
file_filter = nsfw/lang/<lang>/messages.po file_filter = nsfw/lang/<lang>/messages.po
source_file = nsfw/lang/C/messages.po source_file = nsfw/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_numfriends_messagespo] [friendica.addon_numfriends_messagespo]
file_filter = numfriends/lang/<lang>/messages.po file_filter = numfriends/lang/<lang>/messages.po
source_file = numfriends/lang/C/messages.po source_file = numfriends/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_openstreetmap_messagespo] [friendica.addon_openstreetmap_messagespo]
file_filter = openstreetmap/lang/<lang>/messages.po file_filter = openstreetmap/lang/<lang>/messages.po
source_file = openstreetmap/lang/C/messages.po source_file = openstreetmap/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_pageheader_messagespo] [friendica.addon_pageheader_messagespo]
file_filter = pageheader/lang/<lang>/messages.po file_filter = pageheader/lang/<lang>/messages.po
source_file = pageheader/lang/C/messages.po source_file = pageheader/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_piwik_messagespo] [friendica.addon_piwik_messagespo]
file_filter = piwik/lang/<lang>/messages.po file_filter = piwik/lang/<lang>/messages.po
source_file = piwik/lang/C/messages.po source_file = piwik/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_planets_messagespo] [friendica.addon_planets_messagespo]
file_filter = planets/lang/<lang>/messages.po file_filter = planets/lang/<lang>/messages.po
source_file = planets/lang/C/messages.po source_file = planets/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_public_server_messagespo] [friendica.addon_public_server_messagespo]
file_filter = public_server/lang/<lang>/messages.po file_filter = public_server/lang/<lang>/messages.po
source_file = public_server/lang/C/messages.po source_file = public_server/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_pumpio_messagespo] [friendica.addon_pumpio_messagespo]
file_filter = pumpio/lang/<lang>/messages.po file_filter = pumpio/lang/<lang>/messages.po
source_file = pumpio/lang/C/messages.po source_file = pumpio/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_qcomment_messagespo] [friendica.addon_qcomment_messagespo]
file_filter = qcomment/lang/<lang>/messages.po file_filter = qcomment/lang/<lang>/messages.po
source_file = qcomment/lang/C/messages.po source_file = qcomment/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_randplace_messagespo] [friendica.addon_randplace_messagespo]
file_filter = randplace/lang/<lang>/messages.po file_filter = randplace/lang/<lang>/messages.po
source_file = randplace/lang/C/messages.po source_file = randplace/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_remote_permissions_messagespo] [friendica.addon_remote_permissions_messagespo]
file_filter = remote_permissions/lang/<lang>/messages.po file_filter = remote_permissions/lang/<lang>/messages.po
source_file = remote_permissions/lang/C/messages.po source_file = remote_permissions/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_rendertime_messagespo] [friendica.addon_rendertime_messagespo]
file_filter = rendertime/lang/<lang>/messages.po file_filter = rendertime/lang/<lang>/messages.po
source_file = rendertime/lang/C/messages.po source_file = rendertime/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_securemail_messagespo] [friendica.addon_securemail_messagespo]
file_filter = securemail/lang/<lang>/messages.po file_filter = securemail/lang/<lang>/messages.po
source_file = securemail/lang/C/messages.po source_file = securemail/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_showmore_messagespo] [friendica.addon_showmore_messagespo]
file_filter = showmore/lang/<lang>/messages.po file_filter = showmore/lang/<lang>/messages.po
source_file = showmore/lang/C/messages.po source_file = showmore/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_smileybutton_messagespo] [friendica.addon_smileybutton_messagespo]
file_filter = smileybutton/lang/<lang>/messages.po file_filter = smileybutton/lang/<lang>/messages.po
source_file = smileybutton/lang/C/messages.po source_file = smileybutton/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_startpage_messagespo] [friendica.addon_startpage_messagespo]
file_filter = startpage/lang/<lang>/messages.po file_filter = startpage/lang/<lang>/messages.po
source_file = startpage/lang/C/messages.po source_file = startpage/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_statusnet_messagespo] [friendica.addon_statusnet_messagespo]
file_filter = statusnet/lang/<lang>/messages.po file_filter = statusnet/lang/<lang>/messages.po
source_file = statusnet/lang/C/messages.po source_file = statusnet/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_superblock_messagespo] [friendica.addon_superblock_messagespo]
file_filter = superblock/lang/<lang>/messages.po file_filter = superblock/lang/<lang>/messages.po
source_file = superblock/lang/C/messages.po source_file = superblock/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_testdrive_messagespo] [friendica.addon_testdrive_messagespo]
file_filter = testdrive/lang/<lang>/messages.po file_filter = testdrive/lang/<lang>/messages.po
source_file = testdrive/lang/C/messages.po source_file = testdrive/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_tictac_messagespo] [friendica.addon_tictac_messagespo]
file_filter = tictac/lang/<lang>/messages.po file_filter = tictac/lang/<lang>/messages.po
source_file = tictac/lang/C/messages.po source_file = tictac/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_tumblr_messagespo] [friendica.addon_tumblr_messagespo]
file_filter = tumblr/lang/<lang>/messages.po file_filter = tumblr/lang/<lang>/messages.po
source_file = tumblr/lang/C/messages.po source_file = tumblr/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_twitter_messagespo] [friendica.addon_twitter_messagespo]
file_filter = twitter/lang/<lang>/messages.po file_filter = twitter/lang/<lang>/messages.po
source_file = twitter/lang/C/messages.po source_file = twitter/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_viewsrc_messagespo] [friendica.addon_viewsrc_messagespo]
file_filter = viewsrc/lang/<lang>/messages.po file_filter = viewsrc/lang/<lang>/messages.po
source_file = viewsrc/lang/C/messages.po source_file = viewsrc/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_webrtc_messagespo] [friendica.addon_webrtc_messagespo]
file_filter = webrtc/lang/<lang>/messages.po file_filter = webrtc/lang/<lang>/messages.po
source_file = webrtc/lang/C/messages.po source_file = webrtc/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_widgets_messagespo] [friendica.addon_widgets_messagespo]
file_filter = widgets/lang/<lang>/messages.po file_filter = widgets/lang/<lang>/messages.po
source_file = widgets/lang/C/messages.po source_file = widgets/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_windowsphonepush_messagespo] [friendica.addon_windowsphonepush_messagespo]
file_filter = windowsphonepush/lang/<lang>/messages.po file_filter = windowsphonepush/lang/<lang>/messages.po
source_file = windowsphonepush/lang/C/messages.po source_file = windowsphonepush/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_wppost_messagespo] [friendica.addon_wppost_messagespo]
file_filter = wppost/lang/<lang>/messages.po file_filter = wppost/lang/<lang>/messages.po
source_file = wppost/lang/C/messages.po source_file = wppost/lang/C/messages.po
source_lang = en source_lang = en
type = PO type = PO
[o:Friendica:p:friendica:r:addon_xmpp_messagespo] [friendica.addon_xmpp_messagespo]
file_filter = xmpp/lang/<lang>/messages.po file_filter = xmpp/lang/<lang>/messages.po
source_file = xmpp/lang/C/messages.po source_file = xmpp/lang/C/messages.po
source_lang = en source_lang = en

View file

@ -15,7 +15,7 @@ msgstr ""
"POT-Creation-Date: 2022-05-11 08:54-0400\n" "POT-Creation-Date: 2022-05-11 08:54-0400\n"
"PO-Revision-Date: 2018-05-24 06:41+0000\n" "PO-Revision-Date: 2018-05-24 06:41+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2022\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2022\n"
"Language-Team: German (https://app.transifex.com/Friendica/teams/12172/de/)\n" "Language-Team: German (https://www.transifex.com/Friendica/teams/12172/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -3,63 +3,60 @@
# This file is distributed under the same license as the Friendica advancedcontentfilter addon package. # This file is distributed under the same license as the Friendica advancedcontentfilter addon package.
# #
# #
# Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2018
# Sylke Vicious <silkevicious@gmail.com>, 2021
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-05-11 08:54-0400\n" "POT-Creation-Date: 2018-04-17 04:04+0200\n"
"PO-Revision-Date: 2018-05-24 06:41+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n" "Last-Translator: fabrixxm <fabrix.xm@gmail.com>, 2018\n"
"Language-Team: Italian (https://app.transifex.com/Friendica/teams/12172/it/)\n" "Language-Team: Italian (https://www.transifex.com/Friendica/teams/12172/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: advancedcontentfilter.php:154 #: advancedcontentfilter.php:134
#, php-format #, php-format
msgid "Filtered by rule: %s" msgid "Filtered by rule: %s"
msgstr "Filtrato dalla regola: %s" msgstr "Filtrato dalla regola: %s"
#: advancedcontentfilter.php:170 advancedcontentfilter.php:225 #: advancedcontentfilter.php:147 advancedcontentfilter.php:204
msgid "Advanced Content Filter" msgid "Advanced Content Filter"
msgstr "Filtro Avanzato Contenuti" msgstr "Filtro Avanzato Contenuti"
#: advancedcontentfilter.php:224 #: advancedcontentfilter.php:203
msgid "Back to Addon Settings" msgid "Back to Addon Settings"
msgstr "Torna alle impostazioni del componente aggiuntivo" msgstr "Torna alle impostazioni del componente aggiuntivo"
#: advancedcontentfilter.php:226 #: advancedcontentfilter.php:205
msgid "Add a Rule" msgid "Add a Rule"
msgstr "Aggiungi una regola" msgstr "Aggiungi una regola"
#: advancedcontentfilter.php:227 #: advancedcontentfilter.php:206
msgid "Help" msgid "Help"
msgstr "Aiuto" msgstr "Aiuto"
#: advancedcontentfilter.php:228 #: advancedcontentfilter.php:207
msgid "" msgid ""
"Add and manage your personal content filter rules in this screen. Rules have" "Add and manage your personal content filter rules in this screen. Rules have"
" a name and an arbitrary expression that will be matched against post data. " " a name and an arbitrary expression that will be matched against post data. "
"For a complete reference of the available operations and variables, check " "For a complete reference of the available operations and variables, check "
"the help page." "the <a href=\"advancedcontentfilter/help\">help page</a>."
msgstr "" msgstr ""
"Aggiungi e gestisci le tue regole personali di filtro contenuti in questa " "Aggiungi e gestisci le tue regole personali di filtro contenuti in questa "
"schermata. Le regole hanno un nome e un'espressione arbitraria che sarà " "schermata. Le regole hanno un nome e un'espressione arbitraria che sarà "
"confrontata con i dati del messaggio. Per un elenco completo delle " "confrontata con i dati del messaggio. Per un elenco completo delle "
"operazioni e variabili disponibili, controlla la pagina di aiuto." "operazioni disponibili, controlla la <a "
"href=\"advancedcontentfilter/help\">pagina di aiuto</a>."
#: advancedcontentfilter.php:229 #: advancedcontentfilter.php:208
msgid "Your rules" msgid "Your rules"
msgstr "Le tue regole" msgstr "Le tue regole"
#: advancedcontentfilter.php:230 #: advancedcontentfilter.php:209
msgid "" msgid ""
"You have no rules yet! Start adding one by clicking on the button above next" "You have no rules yet! Start adding one by clicking on the button above next"
" to the title." " to the title."
@ -67,106 +64,110 @@ msgstr ""
"Non hai ancora nessuna regola! Aggiungine una cliccando sul bottone qui " "Non hai ancora nessuna regola! Aggiungine una cliccando sul bottone qui "
"sopra a fianco al titolo." "sopra a fianco al titolo."
#: advancedcontentfilter.php:231 #: advancedcontentfilter.php:210
msgid "Disabled" msgid "Disabled"
msgstr "Disabilitato" msgstr "Disabilitato"
#: advancedcontentfilter.php:232 #: advancedcontentfilter.php:211
msgid "Enabled" msgid "Enabled"
msgstr "Abilitato" msgstr "Abilitato"
#: advancedcontentfilter.php:233 #: advancedcontentfilter.php:212
msgid "Disable this rule" msgid "Disable this rule"
msgstr "Diabilita questa regola" msgstr "Diabilita questa regola"
#: advancedcontentfilter.php:234 #: advancedcontentfilter.php:213
msgid "Enable this rule" msgid "Enable this rule"
msgstr "Abilita questa regola" msgstr "Abilita questa regola"
#: advancedcontentfilter.php:235 #: advancedcontentfilter.php:214
msgid "Edit this rule" msgid "Edit this rule"
msgstr "Modifica questa regola" msgstr "Modifica questa regola"
#: advancedcontentfilter.php:236 #: advancedcontentfilter.php:215
msgid "Edit the rule" msgid "Edit the rule"
msgstr "Modifica la regola" msgstr "Modifica la regola"
#: advancedcontentfilter.php:237 #: advancedcontentfilter.php:216
msgid "Save this rule" msgid "Save this rule"
msgstr "Salva questa regola" msgstr "Salva questa regola"
#: advancedcontentfilter.php:238 #: advancedcontentfilter.php:217
msgid "Delete this rule" msgid "Delete this rule"
msgstr "Elimina questa regola" msgstr "Elimina questa regola"
#: advancedcontentfilter.php:239 #: advancedcontentfilter.php:218
msgid "Rule" msgid "Rule"
msgstr "Regola" msgstr "Regola"
#: advancedcontentfilter.php:240 #: advancedcontentfilter.php:219
msgid "Close" msgid "Close"
msgstr "Chiudi" msgstr "Chiudi"
#: advancedcontentfilter.php:241 #: advancedcontentfilter.php:220
msgid "Add new rule" msgid "Add new rule"
msgstr "Aggiungi nuova regola" msgstr "Aggiungi nuova regola"
#: advancedcontentfilter.php:242 #: advancedcontentfilter.php:221
msgid "Rule Name" msgid "Rule Name"
msgstr "Nome Regola" msgstr "Nome Regola"
#: advancedcontentfilter.php:243 #: advancedcontentfilter.php:222
msgid "Rule Expression" msgid "Rule Expression"
msgstr "Espressione Regola" msgstr "Espressione Regola"
#: advancedcontentfilter.php:244 #: advancedcontentfilter.php:223
msgid ""
"<p>Examples:</p><ul><li><pre>author_link == "
"'https://friendica.mrpetovan.com/profile/hypolite'</pre></li><li>tags</li></ul>"
msgstr ""
"<p>Esempi:</p><ul><li><pre>author_link == "
"'https://friendica.mrpetovan.com/profile/hypolite'</pre></li><li>tags</li></ul>"
#: advancedcontentfilter.php:224
msgid "Cancel" msgid "Cancel"
msgstr "Annulla" msgstr "Annulla"
#: advancedcontentfilter.php:295 #: advancedcontentfilter.php:290 advancedcontentfilter.php:301
msgid "This addon requires this node having at least one post" #: advancedcontentfilter.php:312 advancedcontentfilter.php:346
msgstr "" #: advancedcontentfilter.php:375 advancedcontentfilter.php:396
#: advancedcontentfilter.php:325 advancedcontentfilter.php:336
#: advancedcontentfilter.php:347 advancedcontentfilter.php:383
#: advancedcontentfilter.php:414 advancedcontentfilter.php:437
msgid "You must be logged in to use this method" msgid "You must be logged in to use this method"
msgstr "Devi essere autenticato per usare questo metodo" msgstr "Devi essere autenticato per usare questo metodo"
#: advancedcontentfilter.php:351 advancedcontentfilter.php:387 #: advancedcontentfilter.php:316 advancedcontentfilter.php:350
#: advancedcontentfilter.php:418 #: advancedcontentfilter.php:379
msgid "Invalid form security token, please refresh the page." msgid "Invalid form security token, please refresh the page."
msgstr "Token di sicurezza invalido, aggiorna la pagina." msgstr "Token di sicurezza invalido, aggiorna la pagina."
#: advancedcontentfilter.php:363 #: advancedcontentfilter.php:328
msgid "The rule name and expression are required." msgid "The rule name and expression are required."
msgstr "Il nome e l'espressione della regola sono richiesti." msgstr "Il nome e l'espressione della regola sono richiesti."
#: advancedcontentfilter.php:377 #: advancedcontentfilter.php:340
msgid "Rule successfully added" msgid "Rule successfully added"
msgstr "Regola aggiunta con successo" msgstr "Regola aggiunta con successo"
#: advancedcontentfilter.php:391 advancedcontentfilter.php:422 #: advancedcontentfilter.php:354 advancedcontentfilter.php:383
msgid "Rule doesn't exist or doesn't belong to you." msgid "Rule doesn't exist or doesn't belong to you."
msgstr "La regola non esiste o non ti appartiene." msgstr "La regola non esiste o non ti appartiene."
#: advancedcontentfilter.php:408 #: advancedcontentfilter.php:369
msgid "Rule successfully updated" msgid "Rule successfully updated"
msgstr "Regola aggiornata con successo" msgstr "Regola aggiornata con successo"
#: advancedcontentfilter.php:431 #: advancedcontentfilter.php:390
msgid "Rule successfully deleted" msgid "Rule successfully deleted"
msgstr "Regola eliminata con successo" msgstr "Regola eliminata con successo"
#: advancedcontentfilter.php:441 #: advancedcontentfilter.php:400
msgid "Missing argument: guid." msgid "Missing argument: guid."
msgstr "Argomento mancante: guid." msgstr "Argomento mancante: guid."
#: advancedcontentfilter.php:449 #: advancedcontentfilter.php:406
#, php-format #, php-format
msgid "Unknown post with guid: %s" msgid "Unknown post with guid: %s"
msgstr "Messaggio con guid %s sconosciuto" msgstr "Messaggio con guid %s sconosciuto"
#: src/middlewares.php:49 #: src/middlewares.php:28
msgid "Method not found" msgid "Method not found"
msgstr "Metodo non trovato" msgstr "Metodo non trovato"

View file

@ -3,14 +3,14 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Filtered by rule: %s'] = 'Filtrato dalla regola: %s'; $a->strings['Filtered by rule: %s'] = 'Filtrato dalla regola: %s';
$a->strings['Advanced Content Filter'] = 'Filtro Avanzato Contenuti'; $a->strings['Advanced Content Filter'] = 'Filtro Avanzato Contenuti';
$a->strings['Back to Addon Settings'] = 'Torna alle impostazioni del componente aggiuntivo'; $a->strings['Back to Addon Settings'] = 'Torna alle impostazioni del componente aggiuntivo';
$a->strings['Add a Rule'] = 'Aggiungi una regola'; $a->strings['Add a Rule'] = 'Aggiungi una regola';
$a->strings['Help'] = 'Aiuto'; $a->strings['Help'] = 'Aiuto';
$a->strings['Add and manage your personal content filter rules in this screen. Rules have a name and an arbitrary expression that will be matched against post data. For a complete reference of the available operations and variables, check the help page.'] = 'Aggiungi e gestisci le tue regole personali di filtro contenuti in questa schermata. Le regole hanno un nome e un\'espressione arbitraria che sarà confrontata con i dati del messaggio. Per un elenco completo delle operazioni e variabili disponibili, controlla la pagina di aiuto.'; $a->strings['Add and manage your personal content filter rules in this screen. Rules have a name and an arbitrary expression that will be matched against post data. For a complete reference of the available operations and variables, check the <a href="advancedcontentfilter/help">help page</a>.'] = 'Aggiungi e gestisci le tue regole personali di filtro contenuti in questa schermata. Le regole hanno un nome e un\'espressione arbitraria che sarà confrontata con i dati del messaggio. Per un elenco completo delle operazioni disponibili, controlla la <a href="advancedcontentfilter/help">pagina di aiuto</a>.';
$a->strings['Your rules'] = 'Le tue regole'; $a->strings['Your rules'] = 'Le tue regole';
$a->strings['You have no rules yet! Start adding one by clicking on the button above next to the title.'] = 'Non hai ancora nessuna regola! Aggiungine una cliccando sul bottone qui sopra a fianco al titolo.'; $a->strings['You have no rules yet! Start adding one by clicking on the button above next to the title.'] = 'Non hai ancora nessuna regola! Aggiungine una cliccando sul bottone qui sopra a fianco al titolo.';
$a->strings['Disabled'] = 'Disabilitato'; $a->strings['Disabled'] = 'Disabilitato';
@ -26,6 +26,7 @@ $a->strings['Close'] = 'Chiudi';
$a->strings['Add new rule'] = 'Aggiungi nuova regola'; $a->strings['Add new rule'] = 'Aggiungi nuova regola';
$a->strings['Rule Name'] = 'Nome Regola'; $a->strings['Rule Name'] = 'Nome Regola';
$a->strings['Rule Expression'] = 'Espressione Regola'; $a->strings['Rule Expression'] = 'Espressione Regola';
$a->strings['<p>Examples:</p><ul><li><pre>author_link == \'https://friendica.mrpetovan.com/profile/hypolite\'</pre></li><li>tags</li></ul>'] = '<p>Esempi:</p><ul><li><pre>author_link == \'https://friendica.mrpetovan.com/profile/hypolite\'</pre></li><li>tags</li></ul>';
$a->strings['Cancel'] = 'Annulla'; $a->strings['Cancel'] = 'Annulla';
$a->strings['You must be logged in to use this method'] = 'Devi essere autenticato per usare questo metodo'; $a->strings['You must be logged in to use this method'] = 'Devi essere autenticato per usare questo metodo';
$a->strings['Invalid form security token, please refresh the page.'] = 'Token di sicurezza invalido, aggiorna la pagina.'; $a->strings['Invalid form security token, please refresh the page.'] = 'Token di sicurezza invalido, aggiorna la pagina.';

View file

@ -69,7 +69,7 @@ function birdavatar_addon_settings_post(&$s)
} }
if (!empty($_POST['birdavatar-usebird'])) { if (!empty($_POST['birdavatar-usebird'])) {
$url = DI::baseUrl() . '/birdavatar/' . DI::userSession()->getLocalUserId() . '?ts=' . time(); $url = DI::baseUrl()->get() . '/birdavatar/' . DI::userSession()->getLocalUserId() . '?ts=' . time();
$self = DBA::selectFirst('contact', ['id'], ['uid' => DI::userSession()->getLocalUserId(), 'self' => true]); $self = DBA::selectFirst('contact', ['id'], ['uid' => DI::userSession()->getLocalUserId(), 'self' => true]);
if (!DBA::isResult($self)) { if (!DBA::isResult($self)) {
@ -120,9 +120,9 @@ function birdavatar_lookup(array &$b)
{ {
$user = DBA::selectFirst('user', ['uid'], ['email' => $b['email']]); $user = DBA::selectFirst('user', ['uid'], ['email' => $b['email']]);
if (DBA::isResult($user)) { if (DBA::isResult($user)) {
$url = DI::baseUrl() . '/birdavatar/' . $user['uid']; $url = DI::baseUrl()->get() . '/birdavatar/' . $user['uid'];
} else { } else {
$url = DI::baseUrl() . '/birdavatar/' . md5(trim(strtolower($b['email']))); $url = DI::baseUrl()->get() . '/birdavatar/' . md5(trim(strtolower($b['email'])));
} }
switch ($b['size']) { switch ($b['size']) {

View file

@ -12,9 +12,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-22 11:19+0000\n" "PO-Revision-Date: 2021-05-23 19:26+0000\n"
"Last-Translator: Till Mohr <tmtrfx@till-mohr.de>, 2021\n" "Last-Translator: Till Mohr <tmtrfx@till-mohr.de>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -8,7 +8,9 @@
* *
*/ */
use Friendica\App;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\System;
use Friendica\DI; use Friendica\DI;
use Jaybizzle\CrawlerDetect\CrawlerDetect; use Jaybizzle\CrawlerDetect\CrawlerDetect;
use Friendica\Core\Logger; use Friendica\Core\Logger;
@ -50,15 +52,12 @@ function blockbot_init_1()
$logdata = ['agent' => $_SERVER['HTTP_USER_AGENT'], 'uri' => $_SERVER['REQUEST_URI']]; $logdata = ['agent' => $_SERVER['HTTP_USER_AGENT'], 'uri' => $_SERVER['REQUEST_URI']];
// List of "good" crawlers // List of "good" crawlers
$good_agents = [ $good_agents = ['fediverse.space crawler', 'fediverse.network crawler', 'Active_Pods_CheckBot_3.0',
'fediverse.space crawler', 'fediverse.network crawler', 'Active_Pods_CheckBot_3.0',
'Social-Relay/', 'Test Certificate Info', 'Uptimebot/', 'GNUSocialBot', 'UptimeRobot/', 'Social-Relay/', 'Test Certificate Info', 'Uptimebot/', 'GNUSocialBot', 'UptimeRobot/',
'PTST/', 'Zabbix', 'Poduptime/' 'PTST/', 'Zabbix', 'Poduptime/'];
];
// List of known crawlers. // List of known crawlers.
$agents = [ $agents = ['SemrushBot', 's~feedly-nikon3', 'Qwantify/Bleriot/', 'ltx71', 'Sogou web spider/',
'SemrushBot', 's~feedly-nikon3', 'Qwantify/Bleriot/', 'ltx71', 'Sogou web spider/',
'Diffbot/', 'Twitterbot/', 'YisouSpider', 'evc-batch/', 'LivelapBot/', 'TrendsmapResolver/', 'Diffbot/', 'Twitterbot/', 'YisouSpider', 'evc-batch/', 'LivelapBot/', 'TrendsmapResolver/',
'PaperLiBot/', 'Nuzzel', 'um-LN/', 'Google Favicon', 'Datanyze', 'BLEXBot/', '360Spider', 'PaperLiBot/', 'Nuzzel', 'um-LN/', 'Google Favicon', 'Datanyze', 'BLEXBot/', '360Spider',
'adscanner/', 'HeadlessChrome', 'wpif', 'startmebot/', 'Googlebot/', 'Applebot/', 'adscanner/', 'HeadlessChrome', 'wpif', 'startmebot/', 'Googlebot/', 'Applebot/',
@ -80,8 +79,7 @@ function blockbot_init_1()
'Google-Apps-Script; beanserver;', 'woorankreview/', 'Seekport Crawler;', 'AHC/', 'Google-Apps-Script; beanserver;', 'woorankreview/', 'Seekport Crawler;', 'AHC/',
'SkypeUriPreview Preview/', 'Semanticbot/', 'Embed PHP library', 'XoviOnpageCrawler;', 'SkypeUriPreview Preview/', 'Semanticbot/', 'Embed PHP library', 'XoviOnpageCrawler;',
'GetHPinfo.com-Bot/', 'BoardReader Favicon Fetcher', 'Google-Adwords-Instant', 'newspaper/', 'GetHPinfo.com-Bot/', 'BoardReader Favicon Fetcher', 'Google-Adwords-Instant', 'newspaper/',
'YurichevBot/', 'Crawling at Home Project', 'InfoTigerBot/' 'YurichevBot/', 'Crawling at Home Project', 'InfoTigerBot/'];
];
if (!DI::config()->get('blockbot', 'good_crawlers')) { if (!DI::config()->get('blockbot', 'good_crawlers')) {
$agents = array_merge($agents, $good_agents); $agents = array_merge($agents, $good_agents);
@ -116,8 +114,7 @@ function blockbot_init_1()
} }
// List of false positives' strings of known "good" agents. // List of false positives' strings of known "good" agents.
$agents = [ $agents = ['curl', 'zgrab', 'Go-http-client', 'curb', 'github.com', 'reqwest', 'Feedly/',
'curl', 'zgrab', 'Go-http-client', 'curb', 'github.com', 'reqwest', 'Feedly/',
'Python-urllib/', 'Liferea/', 'aiohttp/', 'WordPress.com Reader', 'hackney/', 'Python-urllib/', 'Liferea/', 'aiohttp/', 'WordPress.com Reader', 'hackney/',
'Faraday v', 'okhttp', 'UniversalFeedParser', 'PixelFedBot', 'python-requests', 'Faraday v', 'okhttp', 'UniversalFeedParser', 'PixelFedBot', 'python-requests',
'WordPress/', 'http.rb/', 'Apache-HttpClient/', 'WordPress.com;', 'Pleroma', 'WordPress/', 'http.rb/', 'Apache-HttpClient/', 'WordPress.com;', 'Pleroma',
@ -125,8 +122,7 @@ function blockbot_init_1()
'lua-resty-http/', 'Tiny Tiny RSS/', 'Wget/', 'PostmanRuntime/', 'lua-resty-http/', 'Tiny Tiny RSS/', 'Wget/', 'PostmanRuntime/',
'W3C_Validator/', 'NetNewsWire', 'FeedValidator/', 'theoldreader.com', 'axios/', 'W3C_Validator/', 'NetNewsWire', 'FeedValidator/', 'theoldreader.com', 'axios/',
'Paw/', 'PeerTube/', 'fedi.inex.dev', 'FediDB/', 'index.community crawler', 'Paw/', 'PeerTube/', 'fedi.inex.dev', 'FediDB/', 'index.community crawler',
'Slackbot-LinkExpanding' 'Slackbot-LinkExpanding'];
];
if (DI::config()->get('blockbot', 'good_crawlers')) { if (DI::config()->get('blockbot', 'good_crawlers')) {
$agents = array_merge($agents, $good_agents); $agents = array_merge($agents, $good_agents);
@ -134,11 +130,11 @@ function blockbot_init_1()
foreach ($agents as $agent) { foreach ($agents as $agent) {
if (stristr($_SERVER['HTTP_USER_AGENT'], $agent)) { if (stristr($_SERVER['HTTP_USER_AGENT'], $agent)) {
logger::info('False positive', $logdata); logger::notice('False positive', $logdata);
return; return;
} }
} }
logger::notice('Blocked bot', $logdata); logger::info('Blocked bot', $logdata);
throw new ForbiddenException('Bots are not allowed'); throw new ForbiddenException('Bots are not allowed');
} }

View file

@ -125,7 +125,7 @@ function blockem_prepare_body_content_filter(array &$hook_data)
function blockem_display_item(array &$b = null) function blockem_display_item(array &$b = null)
{ {
if (!empty($b['output']['body']) && strstr($b['output']['body'], 'id="blockem-wrap-')) { if (!empty($b['output']['body']) && strstr($b['output']['body'], 'id="blockem-wrap-')) {
$b['output']['thumb'] = DI::baseUrl() . "/images/person-80.jpg"; $b['output']['thumb'] = DI::baseUrl()->get() . "/images/person-80.jpg";
} }
} }

View file

@ -4,21 +4,21 @@
# #
# #
# Translators: # Translators:
# michal_s <msupler@gmail.com>, 2014 # Michal Šupler <msupler@gmail.com>, 2014
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2014-06-22 13:18+0200\n"
"PO-Revision-Date: 2014-06-22 11:27+0000\n" "PO-Revision-Date: 2014-07-07 18:27+0000\n"
"Last-Translator: michal_s <msupler@gmail.com>, 2014\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: buglink.php:20 #: buglink.php:15
msgid "Report Bug" msgid "Report Bug"
msgstr "Nahlásit chybu" msgstr "Nahlásit chybu"

View file

@ -3,6 +3,6 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['Report Bug'] = 'Nahlásit chybu'; $a->strings['Report Bug'] = 'Nahlásit chybu';

View file

@ -10,16 +10,16 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2014-06-22 13:18+0200\n"
"PO-Revision-Date: 2014-06-22 11:27+0000\n" "PO-Revision-Date: 2019-11-10 20:12+0000\n"
"Last-Translator: René Wagner <apollo@rw-net.de>, 2019\n" "Last-Translator: René Wagner <apollo@rw-net.de>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: buglink.php:20 #: buglink.php:15
msgid "Report Bug" msgid "Report Bug"
msgstr "Fehler melden" msgstr "Fehler melden"

View file

@ -70,7 +70,7 @@ function catavatar_addon_settings_post(&$s)
} }
if (!empty($_POST['catavatar-usecat'])) { if (!empty($_POST['catavatar-usecat'])) {
$url = DI::baseUrl() . '/catavatar/' . DI::userSession()->getLocalUserId() . '?ts=' . time(); $url = DI::baseUrl()->get() . '/catavatar/' . DI::userSession()->getLocalUserId() . '?ts=' . time();
$self = DBA::selectFirst('contact', ['id'], ['uid' => DI::userSession()->getLocalUserId(), 'self' => true]); $self = DBA::selectFirst('contact', ['id'], ['uid' => DI::userSession()->getLocalUserId(), 'self' => true]);
if (!DBA::isResult($self)) { if (!DBA::isResult($self)) {
@ -121,9 +121,9 @@ function catavatar_lookup(array &$b)
{ {
$user = DBA::selectFirst('user', ['uid'], ['email' => $b['email']]); $user = DBA::selectFirst('user', ['uid'], ['email' => $b['email']]);
if (DBA::isResult($user)) { if (DBA::isResult($user)) {
$url = DI::baseUrl() . '/catavatar/' . $user['uid']; $url = DI::baseUrl()->get() . '/catavatar/' . $user['uid'];
} else { } else {
$url = DI::baseUrl() . '/catavatar/' . md5(trim(strtolower($b['email']))); $url = DI::baseUrl()->get() . '/catavatar/' . md5(trim(strtolower($b['email'])));
} }
switch($b['size']) { switch($b['size']) {

View file

@ -15,7 +15,7 @@ msgstr ""
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2018-04-07 05:23+0000\n" "PO-Revision-Date: 2018-04-07 05:23+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2022\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2022\n"
"Language-Team: German (https://app.transifex.com/Friendica/teams/12172/de/)\n" "Language-Team: German (https://www.transifex.com/Friendica/teams/12172/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -6,7 +6,7 @@
# Translators: # Translators:
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2019 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2019
# Ulf Rompe <transifex.com@rompe.org>, 2019 # Ulf Rompe <transifex.com@rompe.org>, 2019
# foss <oss@disr.it>, 2020 # foss <foss@openmailbox.org>, 2020
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@ -15,8 +15,8 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2019-01-23 16:01+0000\n" "PO-Revision-Date: 2019-01-23 16:01+0000\n"
"Last-Translator: foss <oss@disr.it>, 2020\n" "Last-Translator: foss <foss@openmailbox.org>, 2020\n"
"Language-Team: German (https://app.transifex.com/Friendica/teams/12172/de/)\n" "Language-Team: German (https://www.transifex.com/Friendica/teams/12172/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -92,7 +92,7 @@ function curweather_network_mod_init(string &$body)
return; return;
} }
DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . DI::baseUrl() . '/addon/curweather/curweather.css' . '" media="all" />' . "\r\n"; DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . DI::baseUrl()->get() . '/addon/curweather/curweather.css' . '" media="all" />' . "\r\n";
// $rpt value is needed for location // $rpt value is needed for location
// $lang will be taken from the browser session to honour user settings // $lang will be taken from the browser session to honour user settings

View file

@ -11,116 +11,124 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2016-09-25 17:17+0200\n"
"PO-Revision-Date: 2014-06-22 11:34+0000\n" "PO-Revision-Date: 2018-07-05 17:03+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Aditoo\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/Friendica/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: curweather.php:47 #: curweather.php:31
msgid "Error fetching weather data. Error was: " msgid "Error fetching weather data.\\nError was: "
msgstr "" msgstr "Chyba při získávání dat o počasí.\\nChyba:"
#: curweather.php:130 #: curweather.php:111 curweather.php:172
msgid "Current Weather" msgid "Current Weather"
msgstr "Aktuální počasí" msgstr "Aktuální počasí"
#: curweather.php:137 #: curweather.php:118
msgid "Relative Humidity" msgid "Relative Humidity"
msgstr "Relativní vlhkost vzduchu" msgstr "Relativní vlhkost vzduchu"
#: curweather.php:138 #: curweather.php:119
msgid "Pressure" msgid "Pressure"
msgstr "Tlak" msgstr "Tlak"
#: curweather.php:139 #: curweather.php:120
msgid "Wind" msgid "Wind"
msgstr "Vítr" msgstr "Vítr"
#: curweather.php:140 #: curweather.php:121
msgid "Last Updated" msgid "Last Updated"
msgstr "Naposledy aktualizováno" msgstr "Naposledy aktualizováno"
#: curweather.php:141 #: curweather.php:122
msgid "Data by" msgid "Data by"
msgstr "Data podle" msgstr "Data podle"
#: curweather.php:142 #: curweather.php:123
msgid "Show on map" msgid "Show on map"
msgstr "Ukázat na mapě" msgstr "Ukázat na mapě"
#: curweather.php:147 #: curweather.php:128
msgid "There was a problem accessing the weather data. But have a look" msgid "There was a problem accessing the weather data. But have a look"
msgstr "Při získávání dat o počasí nastala chyba. Podívejte se ale" msgstr "Při získávání dat o počasí nastala chyba. Podívejte se ale"
#: curweather.php:149 #: curweather.php:130
msgid "at OpenWeatherMap" msgid "at OpenWeatherMap"
msgstr "na OpenWeatherMap" msgstr "na OpenWeatherMap"
#: curweather.php:178 #: curweather.php:146
msgid "Current Weather settings updated."
msgstr "Nastavení pro Aktuální počasí aktualizováno."
#: curweather.php:161
msgid "No APPID found, please contact your admin to obtain one." msgid "No APPID found, please contact your admin to obtain one."
msgstr "Žádné APPID nebylo nalezeno, prosím kontaktujte svého administrátora pro získání APPID." msgstr "Žádné APPID nebylo nalezeno, prosím kontaktujte svého administrátora pro získání APPID."
#: curweather.php:188 #: curweather.php:171 curweather.php:200
msgid "Save Settings"
msgstr "Uložit nastavení"
#: curweather.php:172
msgid "Settings"
msgstr "Nastavení"
#: curweather.php:174
msgid "Enter either the name of your location or the zip code." msgid "Enter either the name of your location or the zip code."
msgstr "Zadejte buď název místa, kde se nacházíte, nebo PSČ." msgstr "Zadejte buď název místa, kde se nacházíte, nebo PSČ."
#: curweather.php:189 #: curweather.php:175
msgid "Your Location" msgid "Your Location"
msgstr "Vaše poloha" msgstr "Vaše poloha"
#: curweather.php:189 #: curweather.php:175
msgid "" msgid ""
"Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or " "Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or "
"<em>14476,DE</em>." "<em>14476,DE</em>."
msgstr "Identifikátor vaší polohy (název nebo PSČ), např. <em>Praha,CZ</em> nebo <em>11000,CZ</em>." msgstr "Identifikátor vaší polohy (název nebo PSČ), např. <em>Praha,CZ</em> nebo <em>11000,CZ</em>."
#: curweather.php:190 #: curweather.php:176
msgid "Units" msgid "Units"
msgstr "Jednotky" msgstr "Jednotky"
#: curweather.php:190 #: curweather.php:176
msgid "select if the temperature should be displayed in &deg;C or &deg;F" msgid "select if the temperature should be displayed in &deg;C or &deg;F"
msgstr "vyberte, jestli by se teplota měla zobrazovat v &deg;C či &deg;F" msgstr "vyberte, jestli by se teplota měla zobrazovat v &deg;C či &deg;F"
#: curweather.php:191 #: curweather.php:177
msgid "Show weather data" msgid "Show weather data"
msgstr "Ukázat data o počasí" msgstr "Ukázat data o počasí"
#: curweather.php:196 #: curweather.php:190
msgid "Current Weather Settings" msgid "Curweather settings saved."
msgstr "" msgstr "Nastavení Curwather uložena."
#: curweather.php:227 #: curweather.php:201
msgid "Save Settings"
msgstr "Uložit nastavení"
#: curweather.php:230
msgid "Caching Interval" msgid "Caching Interval"
msgstr "Interval mezipaměti" msgstr "Ukládám interval do mezipaměti"
#: curweather.php:232 #: curweather.php:201
msgid "" msgid ""
"For how long should the weather data be cached? Choose according your " "For how long should the weather data be cached? Choose according your "
"OpenWeatherMap account type." "OpenWeatherMap account type."
msgstr "Na jak dlouho by vaše data o počasí měla být uložena v mezipaměti? Vyberte podle typu vašeho účtu na OpenWeatherMap." msgstr "Na jak dlouho by vaše data o počasí měla být uložena v mezipaměti? Vyberte podle typu vašeho účtu na OpenWeatherMap."
#: curweather.php:233 #: curweather.php:201
msgid "no cache" msgid "no cache"
msgstr "žádná mezipaměť" msgstr "žádná mezipaměť"
#: curweather.php:234 curweather.php:235 curweather.php:236 curweather.php:237 #: curweather.php:201
msgid "minutes" msgid "minutes"
msgstr "minut" msgstr "minut"
#: curweather.php:240 #: curweather.php:202
msgid "Your APPID" msgid "Your APPID"
msgstr "Vaše APPID" msgstr "Vaše APPID"
#: curweather.php:240 #: curweather.php:202
msgid "Your API key provided by OpenWeatherMap" msgid "Your API key provided by OpenWeatherMap"
msgstr "Váš API klíč poskytnutý OpenWetherMap" msgstr "Váš API klíč poskytnutý OpenWetherMap"

View file

@ -5,6 +5,7 @@ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; }
}} }}
$a->strings['Error fetching weather data.\nError was: '] = 'Chyba při získávání dat o počasí.\nChyba:';
$a->strings['Current Weather'] = 'Aktuální počasí'; $a->strings['Current Weather'] = 'Aktuální počasí';
$a->strings['Relative Humidity'] = 'Relativní vlhkost vzduchu'; $a->strings['Relative Humidity'] = 'Relativní vlhkost vzduchu';
$a->strings['Pressure'] = 'Tlak'; $a->strings['Pressure'] = 'Tlak';
@ -14,15 +15,18 @@ $a->strings['Data by'] = 'Data podle';
$a->strings['Show on map'] = 'Ukázat na mapě'; $a->strings['Show on map'] = 'Ukázat na mapě';
$a->strings['There was a problem accessing the weather data. But have a look'] = 'Při získávání dat o počasí nastala chyba. Podívejte se ale'; $a->strings['There was a problem accessing the weather data. But have a look'] = 'Při získávání dat o počasí nastala chyba. Podívejte se ale';
$a->strings['at OpenWeatherMap'] = 'na OpenWeatherMap'; $a->strings['at OpenWeatherMap'] = 'na OpenWeatherMap';
$a->strings['Current Weather settings updated.'] = 'Nastavení pro Aktuální počasí aktualizováno.';
$a->strings['No APPID found, please contact your admin to obtain one.'] = 'Žádné APPID nebylo nalezeno, prosím kontaktujte svého administrátora pro získání APPID.'; $a->strings['No APPID found, please contact your admin to obtain one.'] = 'Žádné APPID nebylo nalezeno, prosím kontaktujte svého administrátora pro získání APPID.';
$a->strings['Save Settings'] = 'Uložit nastavení';
$a->strings['Settings'] = 'Nastavení';
$a->strings['Enter either the name of your location or the zip code.'] = 'Zadejte buď název místa, kde se nacházíte, nebo PSČ.'; $a->strings['Enter either the name of your location or the zip code.'] = 'Zadejte buď název místa, kde se nacházíte, nebo PSČ.';
$a->strings['Your Location'] = 'Vaše poloha'; $a->strings['Your Location'] = 'Vaše poloha';
$a->strings['Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or <em>14476,DE</em>.'] = 'Identifikátor vaší polohy (název nebo PSČ), např. <em>Praha,CZ</em> nebo <em>11000,CZ</em>.'; $a->strings['Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or <em>14476,DE</em>.'] = 'Identifikátor vaší polohy (název nebo PSČ), např. <em>Praha,CZ</em> nebo <em>11000,CZ</em>.';
$a->strings['Units'] = 'Jednotky'; $a->strings['Units'] = 'Jednotky';
$a->strings['select if the temperature should be displayed in &deg;C or &deg;F'] = 'vyberte, jestli by se teplota měla zobrazovat v &deg;C či &deg;F'; $a->strings['select if the temperature should be displayed in &deg;C or &deg;F'] = 'vyberte, jestli by se teplota měla zobrazovat v &deg;C či &deg;F';
$a->strings['Show weather data'] = 'Ukázat data o počasí'; $a->strings['Show weather data'] = 'Ukázat data o počasí';
$a->strings['Save Settings'] = 'Uložit nastavení'; $a->strings['Curweather settings saved.'] = 'Nastavení Curwather uložena.';
$a->strings['Caching Interval'] = 'Interval mezipaměti'; $a->strings['Caching Interval'] = 'Ukládám interval do mezipaměti';
$a->strings['For how long should the weather data be cached? Choose according your OpenWeatherMap account type.'] = 'Na jak dlouho by vaše data o počasí měla být uložena v mezipaměti? Vyberte podle typu vašeho účtu na OpenWeatherMap.'; $a->strings['For how long should the weather data be cached? Choose according your OpenWeatherMap account type.'] = 'Na jak dlouho by vaše data o počasí měla být uložena v mezipaměti? Vyberte podle typu vašeho účtu na OpenWeatherMap.';
$a->strings['no cache'] = 'žádná mezipaměť'; $a->strings['no cache'] = 'žádná mezipaměť';
$a->strings['minutes'] = 'minut'; $a->strings['minutes'] = 'minut';

View file

@ -15,9 +15,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-22 11:34+0000\n" "PO-Revision-Date: 2022-01-22 17:28+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2016,2021-2022\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -186,7 +186,7 @@ function diaspora_post_local(array &$b)
function diaspora_send(array &$b) function diaspora_send(array &$b)
{ {
$hostname = DI::baseUrl()->getHost(); $hostname = DI::baseUrl()->getHostname();
Logger::notice('diaspora_send: invoked'); Logger::notice('diaspora_send: invoked');

View file

@ -4,7 +4,7 @@
# #
# #
# Translators: # Translators:
# foss <oss@disr.it>, 2020 # foss <foss@openmailbox.org>, 2020
# Till Mohr <tmtrfx@till-mohr.de>, 2021 # Till Mohr <tmtrfx@till-mohr.de>, 2021
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2014 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2014
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2020 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2020
@ -14,9 +14,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2014-06-22 11:39+0000\n" "PO-Revision-Date: 2021-12-22 15:27+0000\n"
"Last-Translator: Till Mohr <tmtrfx@till-mohr.de>, 2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -10,36 +10,40 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-22 11:41+0000\n" "PO-Revision-Date: 2018-06-14 10:13+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Aditoo\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/Friendica/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: dwpost.php:43 #: dwpost.php:39
msgid "Post to Dreamwidth" msgid "Post to Dreamwidth"
msgstr "Odeslat na Dreamwidth" msgstr "Poslat na Dreamwidth"
#: dwpost.php:63 #: dwpost.php:70
msgid "Enable Dreamwidth Post Addon" msgid "Dreamwidth Post Settings"
msgstr "" msgstr "Nastavení Dreamwidth Post"
#: dwpost.php:64 #: dwpost.php:72
msgid "Dreamwidth username" msgid "Enable dreamwidth Post Addon"
msgstr "" msgstr "Povolit doplněk Dreamwidth Post"
#: dwpost.php:65 #: dwpost.php:77
msgid "Dreamwidth password" msgid "dreamwidth username"
msgstr "" msgstr "dreamwidth uživatelské jméno"
#: dwpost.php:66 #: dwpost.php:82
msgid "Post to Dreamwidth by default" msgid "dreamwidth password"
msgstr "" msgstr "dreamwidth heslo"
#: dwpost.php:71 #: dwpost.php:87
msgid "Dreamwidth Export" msgid "Post to dreamwidth by default"
msgstr "" msgstr "Ve výchozím stavu posílat na dreamwidth"
#: dwpost.php:93
msgid "Submit"
msgstr "Odeslat"

View file

@ -5,4 +5,10 @@ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; }
}} }}
$a->strings['Post to Dreamwidth'] = 'Odeslat na Dreamwidth'; $a->strings['Post to Dreamwidth'] = 'Poslat na Dreamwidth';
$a->strings['Dreamwidth Post Settings'] = 'Nastavení Dreamwidth Post';
$a->strings['Enable dreamwidth Post Addon'] = 'Povolit doplněk Dreamwidth Post';
$a->strings['dreamwidth username'] = 'dreamwidth uživatelské jméno';
$a->strings['dreamwidth password'] = 'dreamwidth heslo';
$a->strings['Post to dreamwidth by default'] = 'Ve výchozím stavu posílat na dreamwidth';
$a->strings['Submit'] = 'Odeslat';

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2014-06-22 11:41+0000\n" "PO-Revision-Date: 2022-01-22 17:33+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021-2022\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -11,9 +11,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-22 12:31+0000\n" "PO-Revision-Date: 2021-02-06 16:55+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-22 12:33+0000\n" "PO-Revision-Date: 2021-12-22 15:15+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2019\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-23 08:27+0000\n" "PO-Revision-Date: 2021-12-22 17:23+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-23 08:30+0000\n" "PO-Revision-Date: 2021-12-22 15:27+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -4,69 +4,72 @@
# #
# #
# Translators: # Translators:
# Aditoo, 2018 # Michal Šupler <msupler@gmail.com>, 2014-2015
# michal_s <msupler@gmail.com>, 2014-2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 08:33+0000\n" "PO-Revision-Date: 2015-02-11 19:41+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: gravatar.php:78 #: gravatar.php:71
msgid "generic profile image" msgid "generic profile image"
msgstr "generický profilový obrázek" msgstr "generický profilový obrázek"
#: gravatar.php:79 #: gravatar.php:72
msgid "random geometric pattern" msgid "random geometric pattern"
msgstr "náhodný geometrický vzor" msgstr "náhodný geometrický vzor"
#: gravatar.php:80 #: gravatar.php:73
msgid "monster face" msgid "monster face"
msgstr "tvář příšery" msgstr "tvář příšery"
#: gravatar.php:81 #: gravatar.php:74
msgid "computer generated face" msgid "computer generated face"
msgstr "počítačově generovaná tvář" msgstr "počítačově generovaná tvář"
#: gravatar.php:82 #: gravatar.php:75
msgid "retro arcade style face" msgid "retro arcade style face"
msgstr "tvář v retro arkádovém stylu" msgstr "tvář v retro arkádovém stylu"
#: gravatar.php:96 #: gravatar.php:89
msgid "Information" msgid "Information"
msgstr "Informace" msgstr "Informace"
#: gravatar.php:96 #: gravatar.php:89
msgid "" msgid ""
"Libravatar addon is installed, too. Please disable Libravatar addon or this " "Libravatar addon is installed, too. Please disable Libravatar addon or this "
"Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if " "Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if "
"nothing was found at Libravatar." "nothing was found at Libravatar."
msgstr "Doplněk Libravatar je také nainstalován. Prosím zakažte doplněk Libravatar nebo tento doplněk Gravatar.<br>Doplněk Libravatar se přepne na Gravatar, pokud na Libravataru nebude nic nalezeno." msgstr "Libravatar doplněk je také nainstalován. Prosím zakažte doplněk Libravatar nebo tento doplněk Gravatar.<br>Libravatar doplněk se vrátí k doplňku Gravatar, pokud na Libravataru nebude nic nalezeno."
#: gravatar.php:102 #: gravatar.php:95
msgid "Save Settings" msgid "Submit"
msgstr "" msgstr "Odeslat"
#: gravatar.php:103 #: gravatar.php:96
msgid "Default avatar image" msgid "Default avatar image"
msgstr "Výchozí avatarový obrázek" msgstr "Defaultní obrázek avataru"
#: gravatar.php:103 #: gravatar.php:96
msgid "Select default avatar image if none was found at Gravatar. See README" msgid "Select default avatar image if none was found at Gravatar. See README"
msgstr "Nastavte výchozí avatarový obrázek, pokud ho již nemáte na Gravataru. Více viz. soubor README." msgstr "Nastavte defaulní obrázek avatara pokud ho již nemáte na Gravatar. Více viz. soubor README."
#: gravatar.php:104 #: gravatar.php:97
msgid "Rating of images" msgid "Rating of images"
msgstr "Hodnocení obrázků" msgstr "Hodnocení obrázků"
#: gravatar.php:104 #: gravatar.php:97
msgid "Select the appropriate avatar rating for your site. See README" msgid "Select the appropriate avatar rating for your site. See README"
msgstr "Zadejte příslušné ohodnocení avataru pro vaši stránku. Viz README." msgstr "Zadejte ohodnocení příslušného avatara pro vaši stránku. Viz README."
#: gravatar.php:111
msgid "Gravatar settings updated."
msgstr "Nastavení Gravatar aktualizováno."

View file

@ -3,7 +3,7 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['generic profile image'] = 'generický profilový obrázek'; $a->strings['generic profile image'] = 'generický profilový obrázek';
$a->strings['random geometric pattern'] = 'náhodný geometrický vzor'; $a->strings['random geometric pattern'] = 'náhodný geometrický vzor';
@ -11,8 +11,10 @@ $a->strings['monster face'] = 'tvář příšery';
$a->strings['computer generated face'] = 'počítačově generovaná tvář'; $a->strings['computer generated face'] = 'počítačově generovaná tvář';
$a->strings['retro arcade style face'] = 'tvář v retro arkádovém stylu'; $a->strings['retro arcade style face'] = 'tvář v retro arkádovém stylu';
$a->strings['Information'] = 'Informace'; $a->strings['Information'] = 'Informace';
$a->strings['Libravatar addon is installed, too. Please disable Libravatar addon or this Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar.'] = 'Doplněk Libravatar je také nainstalován. Prosím zakažte doplněk Libravatar nebo tento doplněk Gravatar.<br>Doplněk Libravatar se přepne na Gravatar, pokud na Libravataru nebude nic nalezeno.'; $a->strings['Libravatar addon is installed, too. Please disable Libravatar addon or this Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar.'] = 'Libravatar doplněk je také nainstalován. Prosím zakažte doplněk Libravatar nebo tento doplněk Gravatar.<br>Libravatar doplněk se vrátí k doplňku Gravatar, pokud na Libravataru nebude nic nalezeno.';
$a->strings['Default avatar image'] = 'Výchozí avatarový obrázek'; $a->strings['Submit'] = 'Odeslat';
$a->strings['Select default avatar image if none was found at Gravatar. See README'] = 'Nastavte výchozí avatarový obrázek, pokud ho již nemáte na Gravataru. Více viz. soubor README.'; $a->strings['Default avatar image'] = 'Defaultní obrázek avataru';
$a->strings['Select default avatar image if none was found at Gravatar. See README'] = 'Nastavte defaulní obrázek avatara pokud ho již nemáte na Gravatar. Více viz. soubor README.';
$a->strings['Rating of images'] = 'Hodnocení obrázků'; $a->strings['Rating of images'] = 'Hodnocení obrázků';
$a->strings['Select the appropriate avatar rating for your site. See README'] = 'Zadejte příslušné ohodnocení avataru pro vaši stránku. Viz README.'; $a->strings['Select the appropriate avatar rating for your site. See README'] = 'Zadejte ohodnocení příslušného avatara pro vaši stránku. Viz README.';
$a->strings['Gravatar settings updated.'] = 'Nastavení Gravatar aktualizováno.';

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 08:33+0000\n" "PO-Revision-Date: 2021-02-06 17:01+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-23 08:35+0000\n" "PO-Revision-Date: 2021-12-22 15:27+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -55,7 +55,7 @@ function ifttt_settings(array &$data)
'new_photo_upload_body' => DI::l10n()->t('Body for "new photo upload"'), 'new_photo_upload_body' => DI::l10n()->t('Body for "new photo upload"'),
'new_link_post_body' => DI::l10n()->t('Body for "new link post"'), 'new_link_post_body' => DI::l10n()->t('Body for "new link post"'),
], ],
'$url' => DI::baseUrl() . '/ifttt/' . DI::userSession()->getLocalUserNickname(), '$url' => DI::baseUrl()->get() . '/ifttt/' . DI::userSession()->getLocalUserNickname(),
'$new_status_message_body' => 'key=' . $key . '&type=status&msg=<<<{{Message}}>>>&date=<<<{{UpdatedAt}}>>>&url=<<<{{PageUrl}}>>>', '$new_status_message_body' => 'key=' . $key . '&type=status&msg=<<<{{Message}}>>>&date=<<<{{UpdatedAt}}>>>&url=<<<{{PageUrl}}>>>',
'$new_photo_upload_body' => 'key=' . $key . '&type=photo&link=<<<{{Link}}>>>&image=<<<{{ImageSource}}>>>&msg=<<<{{Caption}}>>>&date=<<<{{CreatedAt}}>>>&url=<<<{{PageUrl}}>>>', '$new_photo_upload_body' => 'key=' . $key . '&type=photo&link=<<<{{Link}}>>>&image=<<<{{ImageSource}}>>>&msg=<<<{{Caption}}>>>&date=<<<{{CreatedAt}}>>>&url=<<<{{PageUrl}}>>>',
'$new_link_post_body' => 'key=' . $key . '&type=link&link=<<<{{Link}}>>>&title=<<<{{Title}}>>>&msg=<<<{{Message}}>>>&description=<<<{{Description}}>>>&date=<<<{{CreatedAt}}>>>&url=<<<{{PageUrl}}>>>', '$new_link_post_body' => 'key=' . $key . '&type=link&link=<<<{{Link}}>>>&title=<<<{{Title}}>>>&msg=<<<{{Message}}>>>&description=<<<{{Description}}>>>&date=<<<{{CreatedAt}}>>>&url=<<<{{PageUrl}}>>>',

View file

@ -16,7 +16,7 @@ msgstr ""
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2017-11-27 10:37+0000\n" "PO-Revision-Date: 2017-11-27 10:37+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2022\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2022\n"
"Language-Team: German (https://app.transifex.com/Friendica/teams/12172/de/)\n" "Language-Team: German (https://www.transifex.com/Friendica/teams/12172/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2014-06-23 08:37+0000\n" "PO-Revision-Date: 2021-12-22 16:19+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -48,7 +48,7 @@ function impressum_footer(string &$body)
$text = ProxyUtils::proxifyHtml(BBCode::convert(DI::config()->get('impressum', 'footer_text'))); $text = ProxyUtils::proxifyHtml(BBCode::convert(DI::config()->get('impressum', 'footer_text')));
if ($text != '') { if ($text != '') {
DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . DI::baseUrl() . '/addon/impressum/impressum.css" media="all" />'; DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . DI::baseUrl()->get() . '/addon/impressum/impressum.css" media="all" />';
$body .= '<div class="clear"></div>'; $body .= '<div class="clear"></div>';
$body .= '<div id="impressum_footer">' . $text . '</div>'; $body .= '<div id="impressum_footer">' . $text . '</div>';
} }

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 08:39+0000\n" "PO-Revision-Date: 2021-03-29 05:29+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -30,12 +30,13 @@ function infiniteimprobabilitydrive_module() {}
function infiniteimprobabilitydrive_content() function infiniteimprobabilitydrive_content()
{ {
$baseurl = DI::baseUrl()->get() . '/addon/infiniteimprobabilitydrive';
$o = ''; $o = '';
DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'.DI::baseUrl().'/addon/infiniteimprobabilitydrive/infiniteimprobabilitydrive.css"/>'; DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'.DI::baseUrl()->get().'/addon/infiniteimprobabilitydrive/infiniteimprobabilitydrive.css"/>';
$baseurl = (string)DI::baseUrl(); $baseurl = DI::baseUrl()->get();
$o .= <<< EOT $o .= <<< EOT

View file

@ -4,21 +4,21 @@
# #
# #
# Translators: # Translators:
# michal_s <msupler@gmail.com>, 2014 # Michal Šupler <msupler@gmail.com>, 2014
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2014-06-22 13:18+0200\n"
"PO-Revision-Date: 2014-06-23 08:41+0000\n" "PO-Revision-Date: 2014-07-07 19:12+0000\n"
"Last-Translator: michal_s <msupler@gmail.com>, 2014\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: infiniteimprobabilitydrive.php:18 #: infiniteimprobabilitydrive.php:19
msgid "Infinite Improbability Drive" msgid "Infinite Improbability Drive"
msgstr "Infinite Improbability Drive" msgstr "Infinite Improbability Drive"

View file

@ -3,6 +3,6 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['Infinite Improbability Drive'] = 'Infinite Improbability Drive'; $a->strings['Infinite Improbability Drive'] = 'Infinite Improbability Drive';

View file

@ -4,21 +4,21 @@
# #
# #
# Translators: # Translators:
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2014 # bavatar <tobias.diekershoff@gmx.net>, 2014
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2014-06-22 13:18+0200\n"
"PO-Revision-Date: 2014-06-23 08:41+0000\n" "PO-Revision-Date: 2014-07-06 12:15+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2014\n" "Last-Translator: bavatar <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/projects/p/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: infiniteimprobabilitydrive.php:18 #: infiniteimprobabilitydrive.php:19
msgid "Infinite Improbability Drive" msgid "Infinite Improbability Drive"
msgstr "Infinite Improbability Drive" msgstr "Infinite Improbability Drive"

View file

@ -9,16 +9,16 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 08:41+0000\n" "PO-Revision-Date: 2017-09-20 06:08+0000\n"
"Last-Translator: fabrixxm <fabrix.xm@gmail.com>, 2014\n" "Last-Translator: fabrixxm <fabrix.xm@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: infiniteimprobabilitydrive.php:18 #: infiniteimprobabilitydrive.php:19
msgid "Infinite Improbability Drive" msgid "Infinite Improbability Drive"
msgstr "Motore ad Improbabilità Infinita" msgstr "Motore ad Improbabilità Infinita"

View file

@ -3,6 +3,6 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Infinite Improbability Drive'] = 'Motore ad Improbabilità Infinita'; $a->strings['Infinite Improbability Drive'] = 'Motore ad Improbabilità Infinita';

View file

@ -73,7 +73,7 @@ function irc_module() {}
function irc_content() function irc_content()
{ {
$baseurl = DI::baseUrl() . '/addon/irc'; $baseurl = DI::baseUrl()->get() . '/addon/irc';
$o = ''; $o = '';
/* set the list of popular channels */ /* set the list of popular channels */
@ -95,7 +95,7 @@ function irc_content()
DI::page()['aside'] .= '<div class="widget"><h3>' . DI::l10n()->t('Popular Channels') . '</h3><ul>'; DI::page()['aside'] .= '<div class="widget"><h3>' . DI::l10n()->t('Popular Channels') . '</h3><ul>';
foreach ($chats as $chat) { foreach ($chats as $chat) {
DI::page()['aside'] .= '<li><a href="' . DI::baseUrl() . '/irc?channels=' . $chat . '" >' . '#' . $chat . '</a></li>'; DI::page()['aside'] .= '<li><a href="' . DI::baseUrl()->get() . '/irc?channels=' . $chat . '" >' . '#' . $chat . '</a></li>';
} }
DI::page()['aside'] .= '</ul></div>'; DI::page()['aside'] .= '</ul></div>';

View file

@ -11,55 +11,59 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2015-07-08 13:17+0200\n"
"PO-Revision-Date: 2014-06-23 08:41+0000\n" "PO-Revision-Date: 2018-06-14 14:36+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Aditoo\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/Friendica/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: irc.php:32 #: irc.php:37
msgid "IRC Settings"
msgstr "Nastavení IRC"
#: irc.php:38
msgid "" msgid ""
"Here you can change the system wide settings for the channels to " "Here you can change the system wide settings for the channels to "
"automatically join and access via the side bar. Note the changes you do " "automatically join and access via the side bar. Note the changes you do "
"here, only effect the channel selection if you are logged in." "here, only effect the channel selection if you are logged in."
msgstr "Zde můžete změnit systémová nastavení pro kanály pro automatické spojení a přístup přes postranní lištu. Mějte na paměti, že změny, které zde provedete, ovlivní výběr kanálů pouze, pokud jste přihlášen/a." msgstr ""
#: irc.php:33 irc.php:133 #: irc.php:39 irc.php:136
msgid "Save Settings"
msgstr "Uložit nastavení"
#: irc.php:40 irc.php:137
msgid "Channel(s) to auto connect (comma separated)" msgid "Channel(s) to auto connect (comma separated)"
msgstr "Kanál(y) pro automatické připojení (oddělené čárkami)" msgstr "Kanál(y) pro automatické připojení (oddělené čárkami)"
#: irc.php:33 irc.php:133 #: irc.php:40 irc.php:137
msgid "" msgid ""
"List of channels that shall automatically connected to when the app is " "List of channels that shall automatically connected to when the app is "
"launched." "launched."
msgstr "Seznam kanálů, které budou při spuštění aplikace automaticky připojeny." msgstr "Seznam kanálů, které budou při spuštění aplikace automaticky připojeny."
#: irc.php:34 irc.php:134 #: irc.php:41 irc.php:138
msgid "Popular Channels (comma separated)" msgid "Popular Channels (comma separated)"
msgstr "Populární kanály (oddělené čárkami)" msgstr "Populární kanály (oddělené čárkami)"
#: irc.php:34 irc.php:134 #: irc.php:41 irc.php:138
msgid "" msgid ""
"List of popular channels, will be displayed at the side and hotlinked for " "List of popular channels, will be displayed at the side and hotlinked for "
"easy joining." "easy joining."
msgstr "Seznam populárních kanálů, bude zobrazen na straně a bude obsahovat odkazy pro snadné připojení." msgstr "Seznam populárních kanálů, bude zobrazen na straně a bude obsahovat odkazy pro snadné připojení."
#: irc.php:39 #: irc.php:57 irc.php:128
msgid "IRC Settings" msgid "IRC settings saved."
msgstr "Nastavení IRC" msgstr "IRC Nastavení uloženo."
#: irc.php:60 #: irc.php:62
msgid "IRC Chatroom" msgid "IRC Chatroom"
msgstr "IRC Místnost" msgstr "IRC Místnost"
#: irc.php:88 #: irc.php:90
msgid "Popular Channels" msgid "Popular Channels"
msgstr "Populární kanály" msgstr "Populární kanály"
#: irc.php:132
msgid "Save Settings"
msgstr "Uložit nastavení"

View file

@ -5,12 +5,12 @@ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; }
}} }}
$a->strings['Here you can change the system wide settings for the channels to automatically join and access via the side bar. Note the changes you do here, only effect the channel selection if you are logged in.'] = 'Zde můžete změnit systémová nastavení pro kanály pro automatické spojení a přístup přes postranní lištu. Mějte na paměti, že změny, které zde provedete, ovlivní výběr kanálů pouze, pokud jste přihlášen/a.'; $a->strings['IRC Settings'] = 'Nastavení IRC';
$a->strings['Save Settings'] = 'Uložit nastavení';
$a->strings['Channel(s) to auto connect (comma separated)'] = 'Kanál(y) pro automatické připojení (oddělené čárkami)'; $a->strings['Channel(s) to auto connect (comma separated)'] = 'Kanál(y) pro automatické připojení (oddělené čárkami)';
$a->strings['List of channels that shall automatically connected to when the app is launched.'] = 'Seznam kanálů, které budou při spuštění aplikace automaticky připojeny.'; $a->strings['List of channels that shall automatically connected to when the app is launched.'] = 'Seznam kanálů, které budou při spuštění aplikace automaticky připojeny.';
$a->strings['Popular Channels (comma separated)'] = 'Populární kanály (oddělené čárkami)'; $a->strings['Popular Channels (comma separated)'] = 'Populární kanály (oddělené čárkami)';
$a->strings['List of popular channels, will be displayed at the side and hotlinked for easy joining.'] = 'Seznam populárních kanálů, bude zobrazen na straně a bude obsahovat odkazy pro snadné připojení.'; $a->strings['List of popular channels, will be displayed at the side and hotlinked for easy joining.'] = 'Seznam populárních kanálů, bude zobrazen na straně a bude obsahovat odkazy pro snadné připojení.';
$a->strings['IRC Settings'] = 'Nastavení IRC'; $a->strings['IRC settings saved.'] = 'IRC Nastavení uloženo.';
$a->strings['IRC Chatroom'] = 'IRC Místnost'; $a->strings['IRC Chatroom'] = 'IRC Místnost';
$a->strings['Popular Channels'] = 'Populární kanály'; $a->strings['Popular Channels'] = 'Populární kanály';
$a->strings['Save Settings'] = 'Uložit nastavení';

View file

@ -12,9 +12,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-23 08:41+0000\n" "PO-Revision-Date: 2021-02-01 18:19+0000\n"
"Last-Translator: Ulf Rompe <transifex.com@rompe.org>, 2019\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -10,55 +10,59 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2015-07-08 13:17+0200\n"
"PO-Revision-Date: 2014-06-23 08:41+0000\n" "PO-Revision-Date: 2020-09-17 11:40+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2020\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: irc.php:32 #: irc.php:37
msgid "IRC Settings"
msgstr "Impostazioni IRC"
#: irc.php:38
msgid "" msgid ""
"Here you can change the system wide settings for the channels to " "Here you can change the system wide settings for the channels to "
"automatically join and access via the side bar. Note the changes you do " "automatically join and access via the side bar. Note the changes you do "
"here, only effect the channel selection if you are logged in." "here, only effect the channel selection if you are logged in."
msgstr "Qui puoi modificare le impostazioni globali di sistema per i canali a cui accedere automaticamente attraverso la barra laterale. Nota che le modifiche che effetti qui hanno effetto sulla selezione di canali solo se sei loggato." msgstr "Qui puoi modificare le impostazioni globali di sistema per i canali a cui accedere automaticamente attraverso la barra laterale. Nota che le modifiche che effetti qui hanno effetto sulla selezione di canali solo se sei loggato."
#: irc.php:33 irc.php:133 #: irc.php:39 irc.php:136
msgid "Save Settings"
msgstr "Salva Impostazioni"
#: irc.php:40 irc.php:137
msgid "Channel(s) to auto connect (comma separated)" msgid "Channel(s) to auto connect (comma separated)"
msgstr "Canale(i) a cui autocollegarsi (separati da virgola)" msgstr "Canale(i) a cui autocollegarsi (separati da virgola)"
#: irc.php:33 irc.php:133 #: irc.php:40 irc.php:137
msgid "" msgid ""
"List of channels that shall automatically connected to when the app is " "List of channels that shall automatically connected to when the app is "
"launched." "launched."
msgstr "Lista di canali che a cui connettersi automaticamente quando l'app è avviata." msgstr "Lista di canali che a cui connettersi automaticamente quando l'app è avviata."
#: irc.php:34 irc.php:134 #: irc.php:41 irc.php:138
msgid "Popular Channels (comma separated)" msgid "Popular Channels (comma separated)"
msgstr "Canali popolari (separati da virgola)" msgstr "Canali popolari (separati da virgola)"
#: irc.php:34 irc.php:134 #: irc.php:41 irc.php:138
msgid "" msgid ""
"List of popular channels, will be displayed at the side and hotlinked for " "List of popular channels, will be displayed at the side and hotlinked for "
"easy joining." "easy joining."
msgstr "Lista di canali popolari: sarà visualizzata a lato e provvista di collegamento per facilitare l'adesione." msgstr "Lista di canali popolari: sarà visualizzata a lato e provvista di collegamento per facilitare l'adesione."
#: irc.php:39 #: irc.php:57 irc.php:128
msgid "IRC Settings" msgid "IRC settings saved."
msgstr "Impostazioni IRC" msgstr "Impostazioni IRC salvate."
#: irc.php:60 #: irc.php:62
msgid "IRC Chatroom" msgid "IRC Chatroom"
msgstr "Stanza IRC" msgstr "Stanza IRC"
#: irc.php:88 #: irc.php:90
msgid "Popular Channels" msgid "Popular Channels"
msgstr "Canali Popolari" msgstr "Canali Popolari"
#: irc.php:132
msgid "Save Settings"
msgstr "Salva Impostazioni"

View file

@ -3,14 +3,15 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['IRC Settings'] = 'Impostazioni IRC';
$a->strings['Here you can change the system wide settings for the channels to automatically join and access via the side bar. Note the changes you do here, only effect the channel selection if you are logged in.'] = 'Qui puoi modificare le impostazioni globali di sistema per i canali a cui accedere automaticamente attraverso la barra laterale. Nota che le modifiche che effetti qui hanno effetto sulla selezione di canali solo se sei loggato.'; $a->strings['Here you can change the system wide settings for the channels to automatically join and access via the side bar. Note the changes you do here, only effect the channel selection if you are logged in.'] = 'Qui puoi modificare le impostazioni globali di sistema per i canali a cui accedere automaticamente attraverso la barra laterale. Nota che le modifiche che effetti qui hanno effetto sulla selezione di canali solo se sei loggato.';
$a->strings['Save Settings'] = 'Salva Impostazioni';
$a->strings['Channel(s) to auto connect (comma separated)'] = 'Canale(i) a cui autocollegarsi (separati da virgola)'; $a->strings['Channel(s) to auto connect (comma separated)'] = 'Canale(i) a cui autocollegarsi (separati da virgola)';
$a->strings['List of channels that shall automatically connected to when the app is launched.'] = 'Lista di canali che a cui connettersi automaticamente quando l\'app è avviata.'; $a->strings['List of channels that shall automatically connected to when the app is launched.'] = 'Lista di canali che a cui connettersi automaticamente quando l\'app è avviata.';
$a->strings['Popular Channels (comma separated)'] = 'Canali popolari (separati da virgola)'; $a->strings['Popular Channels (comma separated)'] = 'Canali popolari (separati da virgola)';
$a->strings['List of popular channels, will be displayed at the side and hotlinked for easy joining.'] = 'Lista di canali popolari: sarà visualizzata a lato e provvista di collegamento per facilitare l\'adesione.'; $a->strings['List of popular channels, will be displayed at the side and hotlinked for easy joining.'] = 'Lista di canali popolari: sarà visualizzata a lato e provvista di collegamento per facilitare l\'adesione.';
$a->strings['IRC Settings'] = 'Impostazioni IRC'; $a->strings['IRC settings saved.'] = 'Impostazioni IRC salvate.';
$a->strings['IRC Chatroom'] = 'Stanza IRC'; $a->strings['IRC Chatroom'] = 'Stanza IRC';
$a->strings['Popular Channels'] = 'Canali Popolari'; $a->strings['Popular Channels'] = 'Canali Popolari';
$a->strings['Save Settings'] = 'Salva Impostazioni';

View file

@ -4,56 +4,53 @@
# #
# #
# Translators: # Translators:
# Aditoo, 2018 # Michal Šupler <msupler@gmail.com>, 2014
# michal_s <msupler@gmail.com>, 2014
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-23 23:53-0400\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 08:46+0000\n" "PO-Revision-Date: 2014-07-07 19:13+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: js_upload.php:34 #: js_upload.php:43
msgid "Select files for upload" msgid "Upload a file"
msgstr "" msgstr "Nahrát soubor"
#: js_upload.php:35 #: js_upload.php:44
msgid "Drop files here to upload" msgid "Drop files here to upload"
msgstr "Přetáhněte sem soubory k nahrání" msgstr "Přeneste sem soubory k nahrání"
#: js_upload.php:36 #: js_upload.php:45
msgid "Cancel" msgid "Cancel"
msgstr "Zrušit" msgstr "Zrušit"
#: js_upload.php:37 #: js_upload.php:46
msgid "Failed" msgid "Failed"
msgstr "Neúspěch" msgstr "Neúspěch"
#: js_upload.php:215 #: js_upload.php:303
msgid "No files were uploaded." msgid "No files were uploaded."
msgstr "Žádné soubory nebyly nahrány." msgstr "Žádné soubory nebyly nahrány."
#: js_upload.php:221 #: js_upload.php:309
msgid "Uploaded file is empty" msgid "Uploaded file is empty"
msgstr "Nahraný soubor je prázdný" msgstr "Nahraný soubor je prázdný"
#: js_upload.php:233 #: js_upload.php:321
#, php-format msgid "Image exceeds size limit of "
msgid "Image exceeds size limit of %s" msgstr "Velikost obrázku překračuje limit velikosti"
msgstr ""
#: js_upload.php:245 #: js_upload.php:332
#, php-format msgid "File has an invalid extension, it should be one of "
msgid "File has an invalid extension, it should be one of %s." msgstr "Soubor má neplatnou příponu, ta by měla být jednou z"
msgstr ""
#: js_upload.php:256 #: js_upload.php:343
msgid "Upload was cancelled, or server error encountered" msgid "Upload was cancelled, or server error encountered"
msgstr "Nahrávání bylo zrušeno nebo došlo k chybě na serveru" msgstr "Nahrávání bylo zrušeno nebo došlo k chybě na serveru"

View file

@ -3,11 +3,14 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['Drop files here to upload'] = 'Přetáhněte sem soubory k nahrání'; $a->strings['Upload a file'] = 'Nahrát soubor';
$a->strings['Drop files here to upload'] = 'Přeneste sem soubory k nahrání';
$a->strings['Cancel'] = 'Zrušit'; $a->strings['Cancel'] = 'Zrušit';
$a->strings['Failed'] = 'Neúspěch'; $a->strings['Failed'] = 'Neúspěch';
$a->strings['No files were uploaded.'] = 'Žádné soubory nebyly nahrány.'; $a->strings['No files were uploaded.'] = 'Žádné soubory nebyly nahrány.';
$a->strings['Uploaded file is empty'] = 'Nahraný soubor je prázdný'; $a->strings['Uploaded file is empty'] = 'Nahraný soubor je prázdný';
$a->strings['Image exceeds size limit of '] = 'Velikost obrázku překračuje limit velikosti';
$a->strings['File has an invalid extension, it should be one of '] = 'Soubor má neplatnou příponu, ta by měla být jednou z';
$a->strings['Upload was cancelled, or server error encountered'] = 'Nahrávání bylo zrušeno nebo došlo k chybě na serveru'; $a->strings['Upload was cancelled, or server error encountered'] = 'Nahrávání bylo zrušeno nebo došlo k chybě na serveru';

View file

@ -12,9 +12,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-23 23:53-0400\n" "POT-Creation-Date: 2021-03-23 23:53-0400\n"
"PO-Revision-Date: 2014-06-23 08:46+0000\n" "PO-Revision-Date: 2021-12-05 08:34+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -5,55 +5,52 @@
# #
# Translators: # Translators:
# Albert, 2016 # Albert, 2016
# Senex Petrovic <javierruizo@hotmail.com>, 2021
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-23 23:53-0400\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 08:46+0000\n" "PO-Revision-Date: 2016-11-17 18:59+0000\n"
"Last-Translator: Senex Petrovic <javierruizo@hotmail.com>, 2021\n" "Last-Translator: Albert\n"
"Language-Team: Spanish (http://app.transifex.com/Friendica/friendica/language/es/)\n" "Language-Team: Spanish (http://www.transifex.com/Friendica/friendica/language/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: es\n" "Language: es\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: js_upload.php:34 #: js_upload.php:43
msgid "Select files for upload" msgid "Upload a file"
msgstr "Seleciona archivos a subir" msgstr "Subir un archivo"
#: js_upload.php:35 #: js_upload.php:44
msgid "Drop files here to upload" msgid "Drop files here to upload"
msgstr "Soltar archivos aquí para subir" msgstr "Soltar archivos aquí para subir"
#: js_upload.php:36 #: js_upload.php:45
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#: js_upload.php:37 #: js_upload.php:46
msgid "Failed" msgid "Failed"
msgstr "Fallido" msgstr "Fallido"
#: js_upload.php:215 #: js_upload.php:303
msgid "No files were uploaded." msgid "No files were uploaded."
msgstr "No se subió ningún archivo." msgstr "No se subió ningún archivo."
#: js_upload.php:221 #: js_upload.php:309
msgid "Uploaded file is empty" msgid "Uploaded file is empty"
msgstr "El archivo subido está vacío" msgstr "El archivo subido está vacío"
#: js_upload.php:233 #: js_upload.php:321
#, php-format msgid "Image exceeds size limit of "
msgid "Image exceeds size limit of %s" msgstr "La imagen excede el tamaño de"
msgstr ""
#: js_upload.php:245 #: js_upload.php:332
#, php-format msgid "File has an invalid extension, it should be one of "
msgid "File has an invalid extension, it should be one of %s." msgstr "El archivo tiene una extensión inválida, debería ser una de"
msgstr ""
#: js_upload.php:256 #: js_upload.php:343
msgid "Upload was cancelled, or server error encountered" msgid "Upload was cancelled, or server error encountered"
msgstr "La subida fue cancelada, o el servidor tuvo un error" msgstr "La subida fue cancelada, o el servidor tuvo un error"

View file

@ -3,12 +3,14 @@
if(! function_exists("string_plural_select_es")) { if(! function_exists("string_plural_select_es")) {
function string_plural_select_es($n){ function string_plural_select_es($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Select files for upload'] = 'Seleciona archivos a subir'; $a->strings['Upload a file'] = 'Subir un archivo';
$a->strings['Drop files here to upload'] = 'Soltar archivos aquí para subir'; $a->strings['Drop files here to upload'] = 'Soltar archivos aquí para subir';
$a->strings['Cancel'] = 'Cancelar'; $a->strings['Cancel'] = 'Cancelar';
$a->strings['Failed'] = 'Fallido'; $a->strings['Failed'] = 'Fallido';
$a->strings['No files were uploaded.'] = 'No se subió ningún archivo.'; $a->strings['No files were uploaded.'] = 'No se subió ningún archivo.';
$a->strings['Uploaded file is empty'] = 'El archivo subido está vacío'; $a->strings['Uploaded file is empty'] = 'El archivo subido está vacío';
$a->strings['Image exceeds size limit of '] = 'La imagen excede el tamaño de';
$a->strings['File has an invalid extension, it should be one of '] = 'El archivo tiene una extensión inválida, debería ser una de';
$a->strings['Upload was cancelled, or server error encountered'] = 'La subida fue cancelada, o el servidor tuvo un error'; $a->strings['Upload was cancelled, or server error encountered'] = 'La subida fue cancelada, o el servidor tuvo un error';

View file

@ -11,14 +11,14 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-23 23:53-0400\n" "POT-Creation-Date: 2021-03-23 23:53-0400\n"
"PO-Revision-Date: 2014-06-23 08:46+0000\n" "PO-Revision-Date: 2021-05-05 10:50+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: js_upload.php:34 #: js_upload.php:34
msgid "Select files for upload" msgid "Select files for upload"

View file

@ -3,7 +3,7 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Select files for upload'] = 'Seleziona file per il caricamento'; $a->strings['Select files for upload'] = 'Seleziona file per il caricamento';
$a->strings['Drop files here to upload'] = 'Trascina un file qui per caricarlo'; $a->strings['Drop files here to upload'] = 'Trascina un file qui per caricarlo';

View file

@ -5,55 +5,52 @@
# #
# Translators: # Translators:
# Jeroen De Meerleer <me@jeroened.be>, 2018 # Jeroen De Meerleer <me@jeroened.be>, 2018
# Ruud Schilders <ruud@schilders.it>, 2020
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-23 23:53-0400\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 08:46+0000\n" "PO-Revision-Date: 2018-08-24 11:48+0000\n"
"Last-Translator: Ruud Schilders <ruud@schilders.it>, 2020\n" "Last-Translator: Jeroen De Meerleer <me@jeroened.be>\n"
"Language-Team: Dutch (http://app.transifex.com/Friendica/friendica/language/nl/)\n" "Language-Team: Dutch (http://www.transifex.com/Friendica/friendica/language/nl/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: nl\n" "Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: js_upload.php:34 #: js_upload.php:43
msgid "Select files for upload" msgid "Upload a file"
msgstr "" msgstr "Upload een bestand"
#: js_upload.php:35 #: js_upload.php:44
msgid "Drop files here to upload" msgid "Drop files here to upload"
msgstr "Sleep uw bestanden hier om ze te uploaden" msgstr "Sleep uw bestanden hier om ze te uploaden"
#: js_upload.php:36 #: js_upload.php:45
msgid "Cancel" msgid "Cancel"
msgstr "Afbreken" msgstr ""
#: js_upload.php:37 #: js_upload.php:46
msgid "Failed" msgid "Failed"
msgstr "Mislukt" msgstr ""
#: js_upload.php:215 #: js_upload.php:303
msgid "No files were uploaded." msgid "No files were uploaded."
msgstr "Er waren geen bestanden geüpload." msgstr ""
#: js_upload.php:221 #: js_upload.php:309
msgid "Uploaded file is empty" msgid "Uploaded file is empty"
msgstr "Het geüploade bestand is leeg"
#: js_upload.php:233
#, php-format
msgid "Image exceeds size limit of %s"
msgstr "" msgstr ""
#: js_upload.php:245 #: js_upload.php:321
#, php-format msgid "Image exceeds size limit of "
msgid "File has an invalid extension, it should be one of %s."
msgstr "" msgstr ""
#: js_upload.php:256 #: js_upload.php:332
msgid "File has an invalid extension, it should be one of "
msgstr ""
#: js_upload.php:343
msgid "Upload was cancelled, or server error encountered" msgid "Upload was cancelled, or server error encountered"
msgstr "" msgstr ""

View file

@ -5,8 +5,5 @@ function string_plural_select_nl($n){
$n = intval($n); $n = intval($n);
return intval($n != 1); return intval($n != 1);
}} }}
$a->strings['Upload a file'] = 'Upload een bestand';
$a->strings['Drop files here to upload'] = 'Sleep uw bestanden hier om ze te uploaden'; $a->strings['Drop files here to upload'] = 'Sleep uw bestanden hier om ze te uploaden';
$a->strings['Cancel'] = 'Afbreken';
$a->strings['Failed'] = 'Mislukt';
$a->strings['No files were uploaded.'] = 'Er waren geen bestanden geüpload.';
$a->strings['Uploaded file is empty'] = 'Het geüploade bestand is leeg';

View file

@ -14,9 +14,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2015-07-07 15:14+0000\n" "PO-Revision-Date: 2021-12-22 16:22+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -10,20 +10,28 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2015-07-07 15:14+0000\n" "PO-Revision-Date: 2021-04-19 11:11+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: krynn.php:127 #: krynn.php:132 krynn.php:136
msgid "Krynn"
msgstr "Krynn"
#: krynn.php:141
msgid "Krynn Settings"
msgstr "Impostazioni Krynn"
#: krynn.php:143
msgid "Enable Krynn Addon" msgid "Enable Krynn Addon"
msgstr "Abilita componente aggiuntivo Krynn" msgstr "Abilita componente aggiuntivo Krynn"
#: krynn.php:132 #: krynn.php:148
msgid "Krynn Settings" msgid "Save Settings"
msgstr "Impostazioni Krynn" msgstr "Salva Impostazioni"

View file

@ -3,7 +3,9 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Enable Krynn Addon'] = 'Abilita componente aggiuntivo Krynn'; $a->strings['Krynn'] = 'Krynn';
$a->strings['Krynn Settings'] = 'Impostazioni Krynn'; $a->strings['Krynn Settings'] = 'Impostazioni Krynn';
$a->strings['Enable Krynn Addon'] = 'Abilita componente aggiuntivo Krynn';
$a->strings['Save Settings'] = 'Salva Impostazioni';

View file

@ -4,74 +4,73 @@
# #
# #
# Translators: # Translators:
# abidin toumi <abidin24@tutanota.com>, 2021
# Farida Khalaf <faridakhalaf@hotmail.com>, 2021 # Farida Khalaf <faridakhalaf@hotmail.com>, 2021
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2015-07-25 08:05+0000\n" "PO-Revision-Date: 2021-04-26 09:14+0000\n"
"Last-Translator: abidin toumi <abidin24@tutanota.com>, 2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: Arabic (http://app.transifex.com/Friendica/friendica/language/ar/)\n" "Language-Team: Arabic (http://www.transifex.com/Friendica/friendica/language/ar/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: ar\n" "Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#: langfilter.php:49 #: langfilter.php:50
msgid "Language Filter"
msgstr "اللغة"
#: langfilter.php:51
msgid "" msgid ""
"This addon tries to identify the language posts are written in. If it does " "This addon tries to identify the language posts are written in. If it does "
"not match any language specified below, posts will be hidden by collapsing " "not match any language specified below, posts will be hidden by collapsing "
"them." "them."
msgstr "" msgstr ""
#: langfilter.php:50 #: langfilter.php:52
msgid "Use the language filter" msgid "Use the language filter"
msgstr "اختيار اللغة" msgstr "اختيار اللغة"
#: langfilter.php:51 #: langfilter.php:53
msgid "Able to read" msgid "Able to read"
msgstr " قابل للقراءة" msgstr " قابل للقراءة"
#: langfilter.php:51 #: langfilter.php:53
msgid "" msgid ""
"List of abbreviations (ISO 639-1 codes) for languages you speak, comma " "List of abbreviations (ISO 639-1 codes) for languages you speak, comma "
"separated. For example \"de,it\"." "separated. For example \"de,it\"."
msgstr "قائمة الرموز ( ISO 639-1) للغات ، مفصولة بفواصل. على سبيل المثال \"de، it\"" msgstr "قائمة الرموز ( ISO 639-1) للغات ، مفصولة بفواصل. على سبيل المثال \"de، it\""
#: langfilter.php:52 #: langfilter.php:54
msgid "Minimum confidence in language detection" msgid "Minimum confidence in language detection"
msgstr "الحد الأدنى من نسبة اكتشاف اللغة" msgstr "الحد الأدنى من نسبة اكتشاف اللغة"
#: langfilter.php:52 #: langfilter.php:54
msgid "" msgid ""
"Minimum confidence in language detection being correct, from 0 to 100. Posts" "Minimum confidence in language detection being correct, from 0 to 100. Posts"
" will not be filtered when the confidence of language detection is below " " will not be filtered when the confidence of language detection is below "
"this percent value." "this percent value."
msgstr "الحد الأدنى من صحة اكتشاف اللغة ، من 0 إلى 100. لن تتم فلترة المشاركات عندما تكون صحة اكتشاف اللغة أقل من هذه النسبة المئوية." msgstr "الحد الأدنى من صحة اكتشاف اللغة ، من 0 إلى 100. لن تتم فلترة المشاركات عندما تكون صحة اكتشاف اللغة أقل من هذه النسبة المئوية."
#: langfilter.php:53 #: langfilter.php:55
msgid "Minimum length of message body" msgid "Minimum length of message body"
msgstr "الحد الأدنى لنص الرسالة" msgstr "الحد الأدنى لنص الرسالة"
#: langfilter.php:53 #: langfilter.php:55
msgid "" msgid ""
"Minimum number of characters in message body for filter to be used. Posts " "Minimum number of characters in message body for filter to be used. Posts "
"shorter than this will not be filtered. Note: Language detection is " "shorter than this will not be filtered. Note: Language detection is "
"unreliable for short content (<200 characters)." "unreliable for short content (<200 characters)."
msgstr "الحد الأدنى لأحرف نص الرسالة لاستخدام الفلتر. لن يتم فلترة المشاركات الأقصر من هذا. ملاحظة: لا يمكن الاعتماد على اكتشاف اللغة للمحتوى القصير (<200 حرف)." msgstr "الحد الأدنى لأحرف نص الرسالة لاستخدام الفلتر. لن يتم فلترة المشاركات الأقصر من هذا. ملاحظة: لا يمكن الاعتماد على اكتشاف اللغة للمحتوى القصير (<200 حرف)."
#: langfilter.php:58 #: langfilter.php:56
msgid "Language Filter"
msgstr "مرشح اللغة"
#: langfilter.php:60
msgid "Save Settings" msgid "Save Settings"
msgstr "حفظ الإعدادات" msgstr "حفظ الإعدادات"
#: langfilter.php:193 #: langfilter.php:187
#, php-format #, php-format
msgid "Filtered language: %s" msgid "Filtered language: %s"
msgstr "اختيار اللغة: %s" msgstr "اختيار اللغة: %s"

View file

@ -5,6 +5,7 @@ function string_plural_select_ar($n){
$n = intval($n); $n = intval($n);
if ($n==0) { return 0; } else if ($n==1) { return 1; } else if ($n==2) { return 2; } else if ($n%100>=3 && $n%100<=10) { return 3; } else if ($n%100>=11 && $n%100<=99) { return 4; } else { return 5; } if ($n==0) { return 0; } else if ($n==1) { return 1; } else if ($n==2) { return 2; } else if ($n%100>=3 && $n%100<=10) { return 3; } else if ($n%100>=11 && $n%100<=99) { return 4; } else { return 5; }
}} }}
$a->strings['Language Filter'] = 'اللغة';
$a->strings['Use the language filter'] = 'اختيار اللغة'; $a->strings['Use the language filter'] = 'اختيار اللغة';
$a->strings['Able to read'] = ' قابل للقراءة'; $a->strings['Able to read'] = ' قابل للقراءة';
$a->strings['List of abbreviations (ISO 639-1 codes) for languages you speak, comma separated. For example "de,it".'] = 'قائمة الرموز ( ISO 639-1) للغات ، مفصولة بفواصل. على سبيل المثال "de، it"'; $a->strings['List of abbreviations (ISO 639-1 codes) for languages you speak, comma separated. For example "de,it".'] = 'قائمة الرموز ( ISO 639-1) للغات ، مفصولة بفواصل. على سبيل المثال "de، it"';
@ -12,6 +13,5 @@ $a->strings['Minimum confidence in language detection'] = 'الحد الأدنى
$a->strings['Minimum confidence in language detection being correct, from 0 to 100. Posts will not be filtered when the confidence of language detection is below this percent value.'] = 'الحد الأدنى من صحة اكتشاف اللغة ، من 0 إلى 100. لن تتم فلترة المشاركات عندما تكون صحة اكتشاف اللغة أقل من هذه النسبة المئوية.'; $a->strings['Minimum confidence in language detection being correct, from 0 to 100. Posts will not be filtered when the confidence of language detection is below this percent value.'] = 'الحد الأدنى من صحة اكتشاف اللغة ، من 0 إلى 100. لن تتم فلترة المشاركات عندما تكون صحة اكتشاف اللغة أقل من هذه النسبة المئوية.';
$a->strings['Minimum length of message body'] = 'الحد الأدنى لنص الرسالة'; $a->strings['Minimum length of message body'] = 'الحد الأدنى لنص الرسالة';
$a->strings['Minimum number of characters in message body for filter to be used. Posts shorter than this will not be filtered. Note: Language detection is unreliable for short content (<200 characters).'] = 'الحد الأدنى لأحرف نص الرسالة لاستخدام الفلتر. لن يتم فلترة المشاركات الأقصر من هذا. ملاحظة: لا يمكن الاعتماد على اكتشاف اللغة للمحتوى القصير (<200 حرف).'; $a->strings['Minimum number of characters in message body for filter to be used. Posts shorter than this will not be filtered. Note: Language detection is unreliable for short content (<200 characters).'] = 'الحد الأدنى لأحرف نص الرسالة لاستخدام الفلتر. لن يتم فلترة المشاركات الأقصر من هذا. ملاحظة: لا يمكن الاعتماد على اكتشاف اللغة للمحتوى القصير (<200 حرف).';
$a->strings['Language Filter'] = 'مرشح اللغة';
$a->strings['Save Settings'] = 'حفظ الإعدادات'; $a->strings['Save Settings'] = 'حفظ الإعدادات';
$a->strings['Filtered language: %s'] = 'اختيار اللغة: %s'; $a->strings['Filtered language: %s'] = 'اختيار اللغة: %s';

View file

@ -5,7 +5,7 @@
# #
# Translators: # Translators:
# Andreas H., 2018 # Andreas H., 2018
# Copiis Praeesse <copiis@vivaldi.net>, 2018 # Copiis Praeesse <richard@zsemberi.de>, 2018
# Ralf Thees <ralf@herrthees.de>, 2019 # Ralf Thees <ralf@herrthees.de>, 2019
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2015 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2015
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2020-2021 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2020-2021
@ -15,9 +15,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2021-11-21 19:15-0500\n"
"PO-Revision-Date: 2015-07-25 08:05+0000\n" "PO-Revision-Date: 2021-10-19 18:08+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2020-2021\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -12,68 +12,68 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2015-07-25 08:05+0000\n" "PO-Revision-Date: 2021-10-17 21:25+0000\n"
"Last-Translator: SickShark X, 2021\n" "Last-Translator: SickShark X\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: langfilter.php:49 #: langfilter.php:50
msgid "Language Filter"
msgstr "Filtro Lingua"
#: langfilter.php:51
msgid "" msgid ""
"This addon tries to identify the language posts are written in. If it does " "This addon tries to identify the language posts are written in. If it does "
"not match any language specified below, posts will be hidden by collapsing " "not match any language specified below, posts will be hidden by collapsing "
"them." "them."
msgstr "Questo plug-in prova a identificare la lingua con cui sono stati scritti i posts. Se non corrisponde a nessuna delle lingue specificate qui sotto, i post verranno nascosti." msgstr "Questo plug-in prova a identificare la lingua con cui sono stati scritti i posts. Se non corrisponde a nessuna delle lingue specificate qui sotto, i post verranno nascosti."
#: langfilter.php:50 #: langfilter.php:52
msgid "Use the language filter" msgid "Use the language filter"
msgstr "Usa il filtro lingua" msgstr "Usa il filtro lingua"
#: langfilter.php:51 #: langfilter.php:53
msgid "Able to read" msgid "Able to read"
msgstr "In grado di leggere" msgstr "In grado di leggere"
#: langfilter.php:51 #: langfilter.php:53
msgid "" msgid ""
"List of abbreviations (ISO 639-1 codes) for languages you speak, comma " "List of abbreviations (ISO 639-1 codes) for languages you speak, comma "
"separated. For example \"de,it\"." "separated. For example \"de,it\"."
msgstr "Lista di abbreviazioni (codici ISO 639-1) per le lingue che parli, separate da virgola. Per esempio \"it,de\"." msgstr "Lista di abbreviazioni (codici ISO 639-1) per le lingue che parli, separate da virgola. Per esempio \"it,de\"."
#: langfilter.php:52 #: langfilter.php:54
msgid "Minimum confidence in language detection" msgid "Minimum confidence in language detection"
msgstr "Fiducia minima nel rilevamento della lingua" msgstr "Fiducia minima nel rilevamento della lingua"
#: langfilter.php:52 #: langfilter.php:54
msgid "" msgid ""
"Minimum confidence in language detection being correct, from 0 to 100. Posts" "Minimum confidence in language detection being correct, from 0 to 100. Posts"
" will not be filtered when the confidence of language detection is below " " will not be filtered when the confidence of language detection is below "
"this percent value." "this percent value."
msgstr "Fiducia minima che il rilevamento della lingua sia corretto, da 0 a 100. I messaggi non saranno filtrati quando la fiducia nel rilevamento della lingua è sotto questo valore percentuale." msgstr "Fiducia minima che il rilevamento della lingua sia corretto, da 0 a 100. I messaggi non saranno filtrati quando la fiducia nel rilevamento della lingua è sotto questo valore percentuale."
#: langfilter.php:53 #: langfilter.php:55
msgid "Minimum length of message body" msgid "Minimum length of message body"
msgstr "Lunghezza minima del corpo del messaggio" msgstr "Lunghezza minima del corpo del messaggio"
#: langfilter.php:53 #: langfilter.php:55
msgid "" msgid ""
"Minimum number of characters in message body for filter to be used. Posts " "Minimum number of characters in message body for filter to be used. Posts "
"shorter than this will not be filtered. Note: Language detection is " "shorter than this will not be filtered. Note: Language detection is "
"unreliable for short content (<200 characters)." "unreliable for short content (<200 characters)."
msgstr "Numero di caratteri minimo perché il filtro venga usato. I messaggio più corti non saranno filtrati. Nota: la rilevazione della lingua non è affidabile con messaggi brevi (<200 caratteri)" msgstr "Numero di caratteri minimo perché il filtro venga usato. I messaggio più corti non saranno filtrati. Nota: la rilevazione della lingua non è affidabile con messaggi brevi (<200 caratteri)"
#: langfilter.php:58 #: langfilter.php:56
msgid "Language Filter"
msgstr "Filtro Lingua"
#: langfilter.php:60
msgid "Save Settings" msgid "Save Settings"
msgstr "Salva Impostazioni" msgstr "Salva Impostazioni"
#: langfilter.php:193 #: langfilter.php:187
#, php-format #, php-format
msgid "Filtered language: %s" msgid "Filtered language: %s"
msgstr "Lingua filtrata: %s" msgstr "Lingua filtrata: %s"

View file

@ -3,8 +3,9 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Language Filter'] = 'Filtro Lingua';
$a->strings['This addon tries to identify the language posts are written in. If it does not match any language specified below, posts will be hidden by collapsing them.'] = 'Questo plug-in prova a identificare la lingua con cui sono stati scritti i posts. Se non corrisponde a nessuna delle lingue specificate qui sotto, i post verranno nascosti.'; $a->strings['This addon tries to identify the language posts are written in. If it does not match any language specified below, posts will be hidden by collapsing them.'] = 'Questo plug-in prova a identificare la lingua con cui sono stati scritti i posts. Se non corrisponde a nessuna delle lingue specificate qui sotto, i post verranno nascosti.';
$a->strings['Use the language filter'] = 'Usa il filtro lingua'; $a->strings['Use the language filter'] = 'Usa il filtro lingua';
$a->strings['Able to read'] = 'In grado di leggere'; $a->strings['Able to read'] = 'In grado di leggere';
@ -13,6 +14,5 @@ $a->strings['Minimum confidence in language detection'] = 'Fiducia minima nel ri
$a->strings['Minimum confidence in language detection being correct, from 0 to 100. Posts will not be filtered when the confidence of language detection is below this percent value.'] = 'Fiducia minima che il rilevamento della lingua sia corretto, da 0 a 100. I messaggi non saranno filtrati quando la fiducia nel rilevamento della lingua è sotto questo valore percentuale.'; $a->strings['Minimum confidence in language detection being correct, from 0 to 100. Posts will not be filtered when the confidence of language detection is below this percent value.'] = 'Fiducia minima che il rilevamento della lingua sia corretto, da 0 a 100. I messaggi non saranno filtrati quando la fiducia nel rilevamento della lingua è sotto questo valore percentuale.';
$a->strings['Minimum length of message body'] = 'Lunghezza minima del corpo del messaggio'; $a->strings['Minimum length of message body'] = 'Lunghezza minima del corpo del messaggio';
$a->strings['Minimum number of characters in message body for filter to be used. Posts shorter than this will not be filtered. Note: Language detection is unreliable for short content (<200 characters).'] = 'Numero di caratteri minimo perché il filtro venga usato. I messaggio più corti non saranno filtrati. Nota: la rilevazione della lingua non è affidabile con messaggi brevi (<200 caratteri)'; $a->strings['Minimum number of characters in message body for filter to be used. Posts shorter than this will not be filtered. Note: Language detection is unreliable for short content (<200 characters).'] = 'Numero di caratteri minimo perché il filtro venga usato. I messaggio più corti non saranno filtrati. Nota: la rilevazione della lingua non è affidabile con messaggi brevi (<200 caratteri)';
$a->strings['Language Filter'] = 'Filtro Lingua';
$a->strings['Save Settings'] = 'Salva Impostazioni'; $a->strings['Save Settings'] = 'Salva Impostazioni';
$a->strings['Filtered language: %s'] = 'Lingua filtrata: %s'; $a->strings['Filtered language: %s'] = 'Lingua filtrata: %s';

View file

@ -107,7 +107,7 @@ function langfilter_prepare_body_content_filter(&$hook_data)
// Never filter own messages // Never filter own messages
// TODO: find a better way to extract this // TODO: find a better way to extract this
$logged_user_profile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname(); $logged_user_profile = DI::baseUrl()->get() . '/profile/' . DI::userSession()->getLocalUserNickname();
if ($logged_user_profile == $hook_data['item']['author-link']) { if ($logged_user_profile == $hook_data['item']['author-link']) {
return; return;
} }

View file

@ -4,42 +4,45 @@
# #
# #
# Translators: # Translators:
# Aditoo, 2018 # Michal Šupler <msupler@gmail.com>, 2014-2015
# michal_s <msupler@gmail.com>, 2014-2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 09:44+0000\n" "PO-Revision-Date: 2015-02-11 19:39+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: libertree.php:39 #: libertree.php:36
msgid "Post to libertree" msgid "Post to libertree"
msgstr "Poslat na libertree" msgstr "Poslat na libertree"
#: libertree.php:60 #: libertree.php:67
msgid "libertree Post Settings"
msgstr "libertree nastavení příspěvků"
#: libertree.php:69
msgid "Enable Libertree Post Addon" msgid "Enable Libertree Post Addon"
msgstr "Povolit doplněk Libertree Post" msgstr "Povolit Libertree Post rozšíření"
#: libertree.php:61 #: libertree.php:74
msgid "Libertree site URL"
msgstr "URL adresa Libertree "
#: libertree.php:62
msgid "Libertree API token" msgid "Libertree API token"
msgstr "Libertree API token" msgstr "Libertree API token"
#: libertree.php:63 #: libertree.php:79
msgid "Post to Libertree by default" msgid "Libertree site URL"
msgstr "Ve výchozím stavu posílat na Libertree" msgstr "URL adresa Libertree "
#: libertree.php:68 #: libertree.php:84
msgid "Libertree Export" msgid "Post to Libertree by default"
msgstr "" msgstr "Defaultně poslat na Libertree"
#: libertree.php:90
msgid "Submit"
msgstr "Odeslat"

View file

@ -3,10 +3,12 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['Post to libertree'] = 'Poslat na libertree'; $a->strings['Post to libertree'] = 'Poslat na libertree';
$a->strings['Enable Libertree Post Addon'] = 'Povolit doplněk Libertree Post'; $a->strings['libertree Post Settings'] = 'libertree nastavení příspěvků';
$a->strings['Libertree site URL'] = 'URL adresa Libertree '; $a->strings['Enable Libertree Post Addon'] = 'Povolit Libertree Post rozšíření';
$a->strings['Libertree API token'] = 'Libertree API token'; $a->strings['Libertree API token'] = 'Libertree API token';
$a->strings['Post to Libertree by default'] = 'Ve výchozím stavu posílat na Libertree'; $a->strings['Libertree site URL'] = 'URL adresa Libertree ';
$a->strings['Post to Libertree by default'] = 'Defaultně poslat na Libertree';
$a->strings['Submit'] = 'Odeslat';

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2014-06-23 09:44+0000\n" "PO-Revision-Date: 2022-01-22 17:34+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021-2022\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -162,7 +162,7 @@ function libertree_send(array &$b)
$ltree_api_token = DI::pConfig()->get($b['uid'],'libertree','libertree_api_token'); $ltree_api_token = DI::pConfig()->get($b['uid'],'libertree','libertree_api_token');
$ltree_url = DI::pConfig()->get($b['uid'],'libertree','libertree_url'); $ltree_url = DI::pConfig()->get($b['uid'],'libertree','libertree_url');
$ltree_blog = "$ltree_url/api/v1/posts/create/?token=$ltree_api_token"; $ltree_blog = "$ltree_url/api/v1/posts/create/?token=$ltree_api_token";
$ltree_source = DI::baseUrl()->getHost(); $ltree_source = DI::baseUrl()->getHostname();
if ($b['app'] != "") if ($b['app'] != "")
$ltree_source .= " (".$b['app'].")"; $ltree_source .= " (".$b['app'].")";

View file

@ -12,10 +12,10 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2020-12-08 07:16+0000\n"
"PO-Revision-Date: 2014-06-23 09:49+0000\n" "PO-Revision-Date: 2021-02-01 06:30+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -11,14 +11,14 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 09:49+0000\n" "PO-Revision-Date: 2021-05-05 11:06+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: libravatar.php:68 #: libravatar.php:68
msgid "generic profile image" msgid "generic profile image"

View file

@ -3,7 +3,7 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['generic profile image'] = 'immagine generica del profilo'; $a->strings['generic profile image'] = 'immagine generica del profilo';
$a->strings['random geometric pattern'] = 'schema geometrico casuale'; $a->strings['random geometric pattern'] = 'schema geometrico casuale';

View file

@ -4,42 +4,45 @@
# #
# #
# Translators: # Translators:
# Aditoo, 2018 # Michal Šupler <msupler@gmail.com>, 2014-2015
# michal_s <msupler@gmail.com>, 2014-2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 09:51+0000\n" "PO-Revision-Date: 2015-02-11 19:38+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: ljpost.php:43 #: ljpost.php:39
msgid "Post to LiveJournal" msgid "Post to LiveJournal"
msgstr "Odeslat na LiveJournal" msgstr "Poslat na LiveJournal"
#: ljpost.php:63 #: ljpost.php:70
msgid "LiveJournal Post Settings"
msgstr "Nastavení LiveJournal Post"
#: ljpost.php:72
msgid "Enable LiveJournal Post Addon" msgid "Enable LiveJournal Post Addon"
msgstr "Povolit doplněk LiveJournal Post" msgstr "Povolit LiveJournal Post addon"
#: ljpost.php:64 #: ljpost.php:77
msgid "LiveJournal username" msgid "LiveJournal username"
msgstr "LiveJournal uživatelské jméno" msgstr "LiveJournal uživatelské jméno"
#: ljpost.php:65 #: ljpost.php:82
msgid "LiveJournal password" msgid "LiveJournal password"
msgstr "LiveJournal heslo" msgstr "LiveJournal heslo"
#: ljpost.php:66 #: ljpost.php:87
msgid "Post to LiveJournal by default" msgid "Post to LiveJournal by default"
msgstr "Defaultně umístit na LiveJournal" msgstr "Defaultně umístit na LiveJournal"
#: ljpost.php:71 #: ljpost.php:93
msgid "LiveJournal Export" msgid "Submit"
msgstr "" msgstr "Odeslat"

View file

@ -3,10 +3,12 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['Post to LiveJournal'] = 'Odeslat na LiveJournal'; $a->strings['Post to LiveJournal'] = 'Poslat na LiveJournal';
$a->strings['Enable LiveJournal Post Addon'] = 'Povolit doplněk LiveJournal Post'; $a->strings['LiveJournal Post Settings'] = 'Nastavení LiveJournal Post';
$a->strings['Enable LiveJournal Post Addon'] = 'Povolit LiveJournal Post addon';
$a->strings['LiveJournal username'] = 'LiveJournal uživatelské jméno'; $a->strings['LiveJournal username'] = 'LiveJournal uživatelské jméno';
$a->strings['LiveJournal password'] = 'LiveJournal heslo'; $a->strings['LiveJournal password'] = 'LiveJournal heslo';
$a->strings['Post to LiveJournal by default'] = 'Defaultně umístit na LiveJournal'; $a->strings['Post to LiveJournal by default'] = 'Defaultně umístit na LiveJournal';
$a->strings['Submit'] = 'Odeslat';

View file

@ -6,16 +6,16 @@
# Translators: # Translators:
# Andreas H., 2014 # Andreas H., 2014
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2014 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2014
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021-2022 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021
# Ulf Rompe <transifex.com@rompe.org>, 2019 # Ulf Rompe <transifex.com@rompe.org>, 2019
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2014-06-23 09:51+0000\n" "PO-Revision-Date: 2021-12-22 17:23+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018,2021-2022\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -44,4 +44,4 @@ msgstr "Standardmäßig bei LiveJournal veröffentlichen"
#: ljpost.php:71 #: ljpost.php:71
msgid "LiveJournal Export" msgid "LiveJournal Export"
msgstr "LiveJournal Export" msgstr ""

View file

@ -10,4 +10,3 @@ $a->strings['Enable LiveJournal Post Addon'] = 'LiveJournal-Post-Addon aktiviere
$a->strings['LiveJournal username'] = 'LiveJournal-Benutzername'; $a->strings['LiveJournal username'] = 'LiveJournal-Benutzername';
$a->strings['LiveJournal password'] = 'LiveJournal-Passwort'; $a->strings['LiveJournal password'] = 'LiveJournal-Passwort';
$a->strings['Post to LiveJournal by default'] = 'Standardmäßig bei LiveJournal veröffentlichen'; $a->strings['Post to LiveJournal by default'] = 'Standardmäßig bei LiveJournal veröffentlichen';
$a->strings['LiveJournal Export'] = 'LiveJournal Export';

View file

@ -5,41 +5,45 @@
# #
# Translators: # Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2014-2015,2018 # fabrixxm <fabrix.xm@gmail.com>, 2014-2015,2018
# Sylke Vicious <silkevicious@gmail.com>, 2021,2023 # Sylke Vicious <silkevicious@gmail.com>, 2021
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 09:51+0000\n" "PO-Revision-Date: 2021-02-16 12:57+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021,2023\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ljpost.php:43 #: ljpost.php:39
msgid "Post to LiveJournal" msgid "Post to LiveJournal"
msgstr "Invia a LiveJournal" msgstr "Invia a LiveJournal"
#: ljpost.php:63 #: ljpost.php:73
msgid "LiveJournal Post Settings"
msgstr "Impostazioni invio a LiveJournal"
#: ljpost.php:75
msgid "Enable LiveJournal Post Addon" msgid "Enable LiveJournal Post Addon"
msgstr "Abilita il componente aggiuntivo di invio a LiveJournal" msgstr "Abilita il componente aggiuntivo di invio a LiveJournal"
#: ljpost.php:64 #: ljpost.php:80
msgid "LiveJournal username" msgid "LiveJournal username"
msgstr "Nome utente LiveJournal" msgstr "Nome utente LiveJournal"
#: ljpost.php:65 #: ljpost.php:85
msgid "LiveJournal password" msgid "LiveJournal password"
msgstr "Password LiveJournal" msgstr "Password LiveJournal"
#: ljpost.php:66 #: ljpost.php:90
msgid "Post to LiveJournal by default" msgid "Post to LiveJournal by default"
msgstr "Invia sempre a LiveJournal" msgstr "Invia sempre a LiveJournal"
#: ljpost.php:71 #: ljpost.php:96
msgid "LiveJournal Export" msgid "Save Settings"
msgstr "Esporta LiveJournal" msgstr "Salva Impostazioni"

View file

@ -3,11 +3,12 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Post to LiveJournal'] = 'Invia a LiveJournal'; $a->strings['Post to LiveJournal'] = 'Invia a LiveJournal';
$a->strings['LiveJournal Post Settings'] = 'Impostazioni invio a LiveJournal';
$a->strings['Enable LiveJournal Post Addon'] = 'Abilita il componente aggiuntivo di invio a LiveJournal'; $a->strings['Enable LiveJournal Post Addon'] = 'Abilita il componente aggiuntivo di invio a LiveJournal';
$a->strings['LiveJournal username'] = 'Nome utente LiveJournal'; $a->strings['LiveJournal username'] = 'Nome utente LiveJournal';
$a->strings['LiveJournal password'] = 'Password LiveJournal'; $a->strings['LiveJournal password'] = 'Password LiveJournal';
$a->strings['Post to LiveJournal by default'] = 'Invia sempre a LiveJournal'; $a->strings['Post to LiveJournal by default'] = 'Invia sempre a LiveJournal';
$a->strings['LiveJournal Export'] = 'Esporta LiveJournal'; $a->strings['Save Settings'] = 'Salva Impostazioni';

View file

@ -29,72 +29,68 @@ msgstr ""
msgid "Save Settings" msgid "Save Settings"
msgstr "" msgstr ""
#: mailstream.php:311 #: mailstream.php:301
msgid "Re:" msgid "Re:"
msgstr "" msgstr ""
#: mailstream.php:324 mailstream.php:327 #: mailstream.php:314 mailstream.php:317
msgid "Friendica post" msgid "Friendica post"
msgstr "" msgstr ""
#: mailstream.php:330 #: mailstream.php:320
msgid "Diaspora post" msgid "Diaspora post"
msgstr "" msgstr ""
#: mailstream.php:340 #: mailstream.php:330
msgid "Feed item" msgid "Feed item"
msgstr "" msgstr ""
#: mailstream.php:343 #: mailstream.php:333
msgid "Email" msgid "Email"
msgstr "" msgstr ""
#: mailstream.php:345 #: mailstream.php:335
msgid "Friendica Item" msgid "Friendica Item"
msgstr "" msgstr ""
#: mailstream.php:419 #: mailstream.php:404
msgid "Upstream" msgid "Upstream"
msgstr "" msgstr ""
#: mailstream.php:420 #: mailstream.php:405
msgid "URI"
msgstr ""
#: mailstream.php:421
msgid "Local" msgid "Local"
msgstr "" msgstr ""
#: mailstream.php:499 #: mailstream.php:481
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
#: mailstream.php:504 #: mailstream.php:486
msgid "Email Address" msgid "Email Address"
msgstr "" msgstr ""
#: mailstream.php:506 #: mailstream.php:488
msgid "Leave blank to use your account email address" msgid "Leave blank to use your account email address"
msgstr "" msgstr ""
#: mailstream.php:510 #: mailstream.php:492
msgid "Exclude Likes" msgid "Exclude Likes"
msgstr "" msgstr ""
#: mailstream.php:512 #: mailstream.php:494
msgid "Check this to omit mailing \"Like\" notifications" msgid "Check this to omit mailing \"Like\" notifications"
msgstr "" msgstr ""
#: mailstream.php:516 #: mailstream.php:498
msgid "Attach Images" msgid "Attach Images"
msgstr "" msgstr ""
#: mailstream.php:518 #: mailstream.php:500
msgid "" msgid ""
"Download images in posts and attach them to the email. Useful for reading " "Download images in posts and attach them to the email. Useful for reading "
"email while offline." "email while offline."
msgstr "" msgstr ""
#: mailstream.php:525 #: mailstream.php:507
msgid "Mail Stream Settings" msgid "Mail Stream Settings"
msgstr "" msgstr ""

View file

@ -32,6 +32,7 @@ function mailstream_install()
Hook::register('addon_settings_post', 'addon/mailstream/mailstream.php', 'mailstream_addon_settings_post'); Hook::register('addon_settings_post', 'addon/mailstream/mailstream.php', 'mailstream_addon_settings_post');
Hook::register('post_local_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook'); Hook::register('post_local_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook');
Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook'); Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook');
Hook::register('cron', 'addon/mailstream/mailstream.php', 'mailstream_cron');
Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook'); Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook');
Logger::info("mailstream: installed"); Logger::info("mailstream: installed");
@ -66,10 +67,9 @@ function mailstream_module() {}
/** /**
* Adds an item in "addon features" in the admin menu of the site * Adds an item in "addon features" in the admin menu of the site
* *
* @param App $a App object (unused)
* @param string $o HTML form data * @param string $o HTML form data
*/ */
function mailstream_addon_admin(App $a, string &$o) function mailstream_addon_admin(string &$o)
{ {
$frommail = DI::config()->get('mailstream', 'frommail'); $frommail = DI::config()->get('mailstream', 'frommail');
$template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/'); $template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/');
@ -110,7 +110,7 @@ function mailstream_generate_id(string $uri): string
return $message_id; return $message_id;
} }
function mailstream_send_hook(App $a, array $data) function mailstream_send_hook(array $data)
{ {
$criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']); $criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']);
$item = Post::selectFirst([], $criteria); $item = Post::selectFirst([], $criteria);
@ -138,16 +138,15 @@ function mailstream_send_hook(App $a, array $data)
* mailstream is enabled and the necessary data is available, forks a * mailstream is enabled and the necessary data is available, forks a
* workerqueue item to send the email. * workerqueue item to send the email.
* *
* @param App $a App object (unused)
* @param array $item content of the item (may or may not already be stored in the item table) * @param array $item content of the item (may or may not already be stored in the item table)
* @return void * @return void
*/ */
function mailstream_post_hook(App $a, array &$item) function mailstream_post_hook(array &$item)
{ {
mailstream_check_version(); mailstream_check_version();
if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) { if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) {
Logger::debug('mailstream: not enabled.', ['item' => $item['id'], ' uid ' => $item['uid']]); Logger::debug('mailstream: not enabled for item ' . $item['id']);
return; return;
} }
if (!$item['uid']) { if (!$item['uid']) {
@ -162,8 +161,8 @@ function mailstream_post_hook(App $a, array &$item)
Logger::debug('mailstream: no uri for item ' . $item['id']); Logger::debug('mailstream: no uri for item ' . $item['id']);
return; return;
} }
if ($item['verb'] == Activity::ANNOUNCE) { if (!$item['plink']) {
Logger::debug('mailstream: announce item ', ['item' => $item['id']]); Logger::debug('mailstream: no plink for item ' . $item['id']);
return; return;
} }
if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) { if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) {
@ -396,9 +395,7 @@ function mailstream_send(string $message_id, array $item, array $user): bool
$mail->addCustomHeader('In-Reply-To: ' . mailstream_generate_id($item['thr-parent'])); $mail->addCustomHeader('In-Reply-To: ' . mailstream_generate_id($item['thr-parent']));
} }
$mail->addCustomHeader('X-Friendica-Mailstream-URI: ' . $item['uri']); $mail->addCustomHeader('X-Friendica-Mailstream-URI: ' . $item['uri']);
if ($item['plink']) {
$mail->addCustomHeader('X-Friendica-Mailstream-Plink: ' . $item['plink']); $mail->addCustomHeader('X-Friendica-Mailstream-Plink: ' . $item['plink']);
}
$encoding = 'base64'; $encoding = 'base64';
foreach ($attachments as $url => $image) { foreach ($attachments as $url => $image) {
$mail->AddStringEmbeddedImage( $mail->AddStringEmbeddedImage(
@ -417,10 +414,9 @@ function mailstream_send(string $message_id, array $item, array $user): bool
$item['url'] = DI::baseUrl()->get() . '/display/' . $item['guid']; $item['url'] = DI::baseUrl()->get() . '/display/' . $item['guid'];
$mail->Body = Renderer::replaceMacros($template, [ $mail->Body = Renderer::replaceMacros($template, [
'$upstream' => DI::l10n()->t('Upstream'), '$upstream' => DI::l10n()->t('Upstream'),
'$uri' => DI::l10n()->t('URI'),
'$local' => DI::l10n()->t('Local'), '$local' => DI::l10n()->t('Local'),
'$item' => $item]); '$item' => $item]);
$mail->Body = mailstream_html_wrap($mail->Body); mailstream_html_wrap($mail->Body);
if (!$mail->Send()) { if (!$mail->Send()) {
throw new Exception($mail->ErrorInfo); throw new Exception($mail->ErrorInfo);
} }
@ -441,8 +437,7 @@ function mailstream_send(string $message_id, array $item, array $user): bool
* bbcode's output suitable for transmission, we try to break things * bbcode's output suitable for transmission, we try to break things
* up so that lines are about 200 characters. * up so that lines are about 200 characters.
* *
* @param string $text text to word wrap * @param string $text text to word wrap - modified in-place
* @return string wrapped text
*/ */
function mailstream_html_wrap(string &$text) function mailstream_html_wrap(string &$text)
{ {
@ -451,7 +446,6 @@ function mailstream_html_wrap(string &$text)
$lines[$i] = preg_replace('/ /', "\n", $lines[$i], 1); $lines[$i] = preg_replace('/ /', "\n", $lines[$i], 1);
} }
$text = implode($lines); $text = implode($lines);
return $text;
} }
/** /**
@ -468,7 +462,8 @@ function mailstream_convert_table_entries()
'message_id' => $ms_item_id['message-id'], 'message_id' => $ms_item_id['message-id'],
'tries' => 0); 'tries' => 0);
if (!$ms_item_id['message-id'] || !strlen($ms_item_id['message-id'])) { if (!$ms_item_id['message-id'] || !strlen($ms_item_id['message-id'])) {
Logger::info('mailstream_convert_table_entries: item has no message-id.', 'item' => $ms_item_id['id'], 'uri' => $ms_item_id['uri']]); Logger::info('mailstream_cron: Item ' .
$ms_item_id['id'] . ' URI ' . $ms_item_id['uri'] . ' has no message-id');
continue; continue;
} }
Logger::info('mailstream_convert_table_entries: convert item to workerqueue', $send_hook_data); Logger::info('mailstream_convert_table_entries: convert item to workerqueue', $send_hook_data);
@ -480,11 +475,10 @@ function mailstream_convert_table_entries()
/** /**
* Form for configuring mailstream features for a user * Form for configuring mailstream features for a user
* *
* @param App $a App object
* @param array $data Hook data array * @param array $data Hook data array
* @throws \Friendica\Network\HTTPException\ServiceUnavailableException * @throws \Friendica\Network\HTTPException\ServiceUnavailableException
*/ */
function mailstream_addon_settings(App &$a, array &$data) function mailstream_addon_settings(array &$data)
{ {
$enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'mailstream', 'enabled'); $enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'mailstream', 'enabled');
$address = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'mailstream', 'address'); $address = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'mailstream', 'address');
@ -528,11 +522,10 @@ function mailstream_addon_settings(App &$a, array &$data)
/** /**
* Process data submitted to user's mailstream features form * Process data submitted to user's mailstream features form
* @param App $a
* @param array $post POST data * @param array $post POST data
* @return void * @return void
*/ */
function mailstream_addon_settings_post(App $a, array $post) function mailstream_addon_settings_post(array $post)
{ {
if (!DI::userSession()->getLocalUserId() || empty($post['mailstream-submit'])) { if (!DI::userSession()->getLocalUserId() || empty($post['mailstream-submit'])) {
return; return;

View file

@ -6,6 +6,5 @@
<div class="mailstream-item-body">{{$item.body nofilter}}</div> <div class="mailstream-item-body">{{$item.body nofilter}}</div>
{{if $item.plink}} {{if $item.plink}}
<div>{{$upstream}}: <a class="mailstream-item-plink" href="{{$item.plink}}">{{$item.plink}}</a><div> <div>{{$upstream}}: <a class="mailstream-item-plink" href="{{$item.plink}}">{{$item.plink}}</a><div>
<div>{{$uri}}: <a class="mailstream-item-uri" href="{{$item.uri}}">{{$item.uri}}</a><div>
<div>{{$local}}: <a class="mailstream-item-url" href="{{$item.url}}">{{$item.url}}</a></div> <div>{{$local}}: <a class="mailstream-item-url" href="{{$item.url}}">{{$item.url}}</a></div>
{{/if}} {{/if}}

View file

@ -4,29 +4,50 @@
# #
# #
# Translators: # Translators:
# Aditoo, 2018 # Michal Šupler <msupler@gmail.com>, 2014-2015
# michal_s <msupler@gmail.com>, 2014-2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2013-02-27 05:01-0500\n"
"PO-Revision-Date: 2014-06-23 09:55+0000\n" "PO-Revision-Date: 2015-02-11 19:38+0000\n"
"Last-Translator: Aditoo, 2018\n" "Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n" "Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: mathjax.php:42 #: mathjax.php:36
msgid "Settings"
msgstr "Nastavení"
#: mathjax.php:37
msgid "" msgid ""
"The MathJax addon renders mathematical formulae written using the LaTeX " "The MathJax addon renders mathematical formulae written using the LaTeX "
"syntax surrounded by the usual $$ or an eqnarray block in the postings of " "syntax surrounded by the usual $$ or an eqnarray block in the postings of "
"your wall,network tab and private mail." "your wall,network tab and private mail."
msgstr "Doplněk MathJax vykresluje matematické vzorce zapsané s použitím syntaxe LaTeX označené obvyklými znaky $$, nebo blok \"eqnarray\" v příspěvcích na Vaší zdi, záložce Síť a soukromých zprávách." msgstr "Rozšíření MathJax vykresluje matematické vzorce zapsané s použitím syntaxe LaTeX označené obvyklými znaky $$ nebo v bloku \"eqnarray\" v příspěvcích na vaší zdi, záložce síť a soukromých zprávách."
#: mathjax.php:43 #: mathjax.php:38
msgid "Use the MathJax renderer" msgid "Use the MathJax renderer"
msgstr "Použít vykreslování MathJax" msgstr "Použít Mathjax vykreslování"
#: mathjax.php:42
msgid "Submit"
msgstr "Odeslat"
#: mathjax.php:66
msgid "Settings updated."
msgstr "Nastavení aktualizováno."
#: mathjax.php:75
msgid "MathJax Base URL"
msgstr "Základní MathJax adresa URL"
#: mathjax.php:75
msgid ""
"The URL for the javascript file that should be included to use MathJax. Can "
"be either the MathJax CDN or another installation of MathJax."
msgstr "URL adresa na javascriptový soubor, který musí být obsažen pro použití MathJax. Může to být MathJax CDN nebo or jiná instalace MathJax."

View file

@ -3,7 +3,12 @@
if(! function_exists("string_plural_select_cs")) { if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){ function string_plural_select_cs($n){
$n = intval($n); $n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; } if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}} }}
$a->strings['The MathJax addon renders mathematical formulae written using the LaTeX syntax surrounded by the usual $$ or an eqnarray block in the postings of your wall,network tab and private mail.'] = 'Doplněk MathJax vykresluje matematické vzorce zapsané s použitím syntaxe LaTeX označené obvyklými znaky $$, nebo blok "eqnarray" v příspěvcích na Vaší zdi, záložce Síť a soukromých zprávách.'; $a->strings['Settings'] = 'Nastavení';
$a->strings['Use the MathJax renderer'] = 'Použít vykreslování MathJax'; $a->strings['The MathJax addon renders mathematical formulae written using the LaTeX syntax surrounded by the usual $$ or an eqnarray block in the postings of your wall,network tab and private mail.'] = 'Rozšíření MathJax vykresluje matematické vzorce zapsané s použitím syntaxe LaTeX označené obvyklými znaky $$ nebo v bloku "eqnarray" v příspěvcích na vaší zdi, záložce síť a soukromých zprávách.';
$a->strings['Use the MathJax renderer'] = 'Použít Mathjax vykreslování';
$a->strings['Submit'] = 'Odeslat';
$a->strings['Settings updated.'] = 'Nastavení aktualizováno.';
$a->strings['MathJax Base URL'] = 'Základní MathJax adresa URL';
$a->strings['The URL for the javascript file that should be included to use MathJax. Can be either the MathJax CDN or another installation of MathJax.'] = 'URL adresa na javascriptový soubor, který musí být obsažen pro použití MathJax. Může to být MathJax CDN nebo or jiná instalace MathJax.';

View file

@ -13,9 +13,9 @@ msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2021-11-21 19:15-0500\n"
"PO-Revision-Date: 2014-06-23 09:55+0000\n" "PO-Revision-Date: 2021-12-22 16:24+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n" "Last-Translator: Transifex Bot <>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View file

@ -10,23 +10,27 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 09:55+0000\n" "PO-Revision-Date: 2021-02-16 12:51+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: mathjax.php:42 #: mathjax.php:43
msgid "" msgid ""
"The MathJax addon renders mathematical formulae written using the LaTeX " "The MathJax addon renders mathematical formulae written using the LaTeX "
"syntax surrounded by the usual $$ or an eqnarray block in the postings of " "syntax surrounded by the usual $$ or an eqnarray block in the postings of "
"your wall,network tab and private mail." "your wall,network tab and private mail."
msgstr "Il plugin \"MatJax\" renderizza formule matematiche scritta usando la sintassi LaTeX circondate dalle usuali $$ o un blocco eqnarray nei messaggi della tua bacheca, pagina Rete e messaggi privati." msgstr "Il plugin \"MatJax\" renderizza formule matematiche scritta usando la sintassi LaTeX circondate dalle usuali $$ o un blocco eqnarray nei messaggi della tua bacheca, pagina Rete e messaggi privati."
#: mathjax.php:43 #: mathjax.php:44
msgid "Use the MathJax renderer" msgid "Use the MathJax renderer"
msgstr "Usa il render MathJax" msgstr "Usa il render MathJax"
#: mathjax.php:45
msgid "Save Settings"
msgstr "Salva Impostazioni"

View file

@ -3,7 +3,8 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['The MathJax addon renders mathematical formulae written using the LaTeX syntax surrounded by the usual $$ or an eqnarray block in the postings of your wall,network tab and private mail.'] = 'Il plugin "MatJax" renderizza formule matematiche scritta usando la sintassi LaTeX circondate dalle usuali $$ o un blocco eqnarray nei messaggi della tua bacheca, pagina Rete e messaggi privati.'; $a->strings['The MathJax addon renders mathematical formulae written using the LaTeX syntax surrounded by the usual $$ or an eqnarray block in the postings of your wall,network tab and private mail.'] = 'Il plugin "MatJax" renderizza formule matematiche scritta usando la sintassi LaTeX circondate dalle usuali $$ o un blocco eqnarray nei messaggi della tua bacheca, pagina Rete e messaggi privati.';
$a->strings['Use the MathJax renderer'] = 'Usa il render MathJax'; $a->strings['Use the MathJax renderer'] = 'Usa il render MathJax';
$a->strings['Save Settings'] = 'Salva Impostazioni';

View file

@ -11,8 +11,8 @@
} }
], ],
"require": { "require": {
"php": ">=7.0", "php": ">=7.3",
"monolog/monolog": "^3.2" "monolog/monolog": "^2.8"
}, },
"license": "3-clause BSD license", "license": "3-clause BSD license",
"config": { "config": {

51
monolog/composer.lock generated
View file

@ -4,31 +4,31 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "e812bcd051a73d1c9b19c91ec88a6a21", "content-hash": "d360e9eb0b5580e44579a3780be74dec",
"packages": [ "packages": [
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.2.0", "version": "2.8.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Seldaek/monolog.git", "url": "https://github.com/Seldaek/monolog.git",
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81" "reference": "720488632c590286b88b80e62aa3d3d551ad4a50"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/305444bc6fb6c89e490f4b34fa6e979584d7fa81", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50",
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81", "reference": "720488632c590286b88b80e62aa3d3d551ad4a50",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=7.2",
"psr/log": "^2.0 || ^3.0" "psr/log": "^1.0.1 || ^2.0 || ^3.0"
}, },
"provide": { "provide": {
"psr/log-implementation": "3.0.0" "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
}, },
"require-dev": { "require-dev": {
"aws/aws-sdk-php": "^3.0", "aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev", "doctrine/couchdb": "~1.0@dev",
"elasticsearch/elasticsearch": "^7 || ^8", "elasticsearch/elasticsearch": "^7 || ^8",
"ext-json": "*", "ext-json": "*",
@ -37,12 +37,13 @@
"guzzlehttp/psr7": "^2.2", "guzzlehttp/psr7": "^2.2",
"mongodb/mongodb": "^1.8", "mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4 || ^3", "php-amqplib/php-amqplib": "~2.4 || ^3",
"phpstan/phpstan": "^1.4", "phpspec/prophecy": "^1.15",
"phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan": "^0.12.91",
"phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8.5.14",
"phpunit/phpunit": "^9.5.16", "predis/predis": "^1.1 || ^2.0",
"predis/predis": "^1.1", "rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": "^7", "ruflin/elastica": "^7",
"swiftmailer/swiftmailer": "^5.3|^6.0",
"symfony/mailer": "^5.4 || ^6", "symfony/mailer": "^5.4 || ^6",
"symfony/mime": "^5.4 || ^6" "symfony/mime": "^5.4 || ^6"
}, },
@ -65,7 +66,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.x-dev" "dev-main": "2.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -101,34 +102,34 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-07-24T12:00:55+00:00" "time": "2022-07-24T11:55:47+00:00"
}, },
{ {
"name": "psr/log", "name": "psr/log",
"version": "3.0.0", "version": "1.1.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/log.git", "url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.0.0" "php": ">=5.3.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.x-dev" "dev-master": "1.1.x-dev"
} }
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Psr\\Log\\": "src" "Psr\\Log\\": "Psr/Log/"
} }
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@ -148,7 +149,7 @@
"psr", "psr",
"psr-3" "psr-3"
], ],
"time": "2021-07-14T16:46:02+00:00" "time": "2021-05-03T11:20:27+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
@ -158,7 +159,7 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.0" "php": ">=7.3"
}, },
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "1.1.0" "plugin-api-version": "1.1.0"

View file

@ -1,66 +0,0 @@
<?php
/*
* Name: Monolog
* Description: A Logging framework with lots of additions (see [Monolog](https://github.com/Seldaek/monolog/)). There are just Friendica additions inside the src directory
* Version: 1.0
* Author: Philipp Holzer
*/
use Friendica\App;
use Friendica\Core\Hook;
use Friendica\Addon\monolog\src\IntrospectionProcessor;
use Friendica\DI;
use Psr\Log\LogLevel;
require_once __DIR__ . '/vendor/autoload.php';
function monolog_install()
{
Hook::register('logger_instance' , __FILE__, 'monolog_instance');
}
function monolog_uninstall()
{
Hook::unregister('logger_instance', __FILE__, 'monolog_instance');
}
function monolog_instance(array &$data)
{
if ($data['name'] !== 'monolog') {
return;
}
$loggerTimeZone = new \DateTimeZone('UTC');
$logger = new Monolog\Logger($data['channel']);
$logger->setTimezone($loggerTimeZone);
$logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
$logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushProcessor(new IntrospectionProcessor($data['introspection'], LogLevel::DEBUG));
$stream = DI::config()->get('system', 'logfile');
// just add a stream in case it's either writable or not file
if (!is_file($stream) || is_writable($stream)) {
try {
$loglevel = Monolog\Logger::toMonologLevel($data['loglevel']);
// fallback to notice if an invalid loglevel is set
if (!is_int($loglevel)) {
$loglevel = LogLevel::NOTICE;
}
$fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
$formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
$fileHandler->setFormatter($formatter);
$logger->pushHandler($fileHandler);
} catch (\Throwable $e) {
return;
}
}
$data['storage'] = $logger;
}

View file

@ -0,0 +1,90 @@
<?php
/**
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Addon\monolog\src\Factory;
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
use Friendica\Core\Logger\Exception\LoggerException;
use Monolog as MonologModel;
use Friendica\Addon\monolog\src\Monolog\IntrospectionProcessor;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
class Monolog implements IAmAStrategy
{
/** @var IManageConfigValues */
protected $config;
/** @var string */
protected $channel = '';
/** @var string */
protected $loglevel = LogLevel::NOTICE;
/** @var IHaveCallIntrospections */
protected $introspection;
public function __construct(IManageConfigValues $config, IHaveCallIntrospections $introspection, string $channel = '', string $loglevel = LogLevel::NOTICE)
{
$this->config = $config;
$this->channel = $channel;
$this->loglevel = $loglevel;
$this->introspection = $introspection;
}
public function create(): LoggerInterface
{
$loggerTimeZone = new \DateTimeZone('UTC');
$logger = new MonologModel\Logger($this->channel);
$logger->setTimezone($loggerTimeZone);
$logger->pushProcessor(new MonologModel\Processor\PsrLogMessageProcessor());
$logger->pushProcessor(new MonologModel\Processor\ProcessIdProcessor());
$logger->pushProcessor(new MonologModel\Processor\UidProcessor());
$logger->pushProcessor(new IntrospectionProcessor($this->introspection, LogLevel::DEBUG));
$stream = $this->config->get('system', 'logfile');
// just add a stream in case it's either writable or not file
if (!is_file($stream) || is_writable($stream)) {
try {
$loglevel = MonologModel\Logger::toMonologLevel($this->loglevel);
// fallback to notice if an invalid loglevel is set
if (!is_int($loglevel)) {
$loglevel = LogLevel::NOTICE;
}
$fileHandler = new MonologModel\Handler\StreamHandler($stream, $loglevel);
$formatter = new MonologModel\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
$fileHandler->setFormatter($formatter);
$logger->pushHandler($fileHandler);
return $logger;
} catch (\Throwable $e) {
throw new LoggerException('Cannot create Loger', $e);
}
} else {
throw new LoggerException(sprintf('Cannot write to file or stream %s', $stream));
}
}
}

View file

@ -19,7 +19,7 @@
* *
*/ */
namespace Friendica\Addon\monolog\src; namespace Friendica\Addon\monolog\src\Monolog;
use Friendica\App\Request; use Friendica\App\Request;
use Monolog\Handler; use Monolog\Handler;

View file

@ -19,11 +19,10 @@
* *
*/ */
namespace Friendica\Addon\monolog\src; namespace Friendica\Addon\monolog\src\Monolog;
use Friendica\Core\Logger\Util\Introspection; use Friendica\Core\Logger\Util\Introspection;
use Monolog\Logger; use Monolog\Logger;
use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface; use Monolog\Processor\ProcessorInterface;
/** /**
@ -42,19 +41,19 @@ class IntrospectionProcessor implements ProcessorInterface
public function __construct(Introspection $introspection, $level = Logger::DEBUG) public function __construct(Introspection $introspection, $level = Logger::DEBUG)
{ {
$this->level = Logger::toMonologLevel($level); $this->level = Logger::toMonologLevel($level);
$introspection->addClasses(['Monolog\\']); $introspection->addClasses(['Monolog\\', IntrospectionProcessor::class]);
$this->introspection = $introspection; $this->introspection = $introspection;
} }
public function __invoke(LogRecord $record): LogRecord public function __invoke(array $record): array
{ {
// return if the level is not high enough // return if the level is not high enough
if ($record->level < $this->level) { if ($record['level'] < $this->level) {
return $record; return $record;
} }
// we should have the call source now // we should have the call source now
$record->extra = array_merge( $record['extra'] = array_merge(
$record->extra, $record['extra'],
$this->introspection->getRecord() $this->introspection->getRecord()
); );

View file

@ -0,0 +1,35 @@
<?php
/**
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
$ADDON = [
'name' => 'monolog',
'description' => 'Monolog can send your logs to files, sockets, inboxes, database, etc..',
'author' => [
'name' => 'Philipp Holzer',
'email' => 'admin@philipp.info',
],
'maintainer' => [
'name' => 'Philipp Holzer',
'email' => 'admin@philipp.info',
],
'version' => '1.0',
'status' => 'supported',
];

View file

@ -0,0 +1,31 @@
<?php
/**
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use Dice\Dice;
use Friendica\Addon\monolog\src\Factory\Monolog;
return [
Monolog::class => [
'call' => [
['create', [], Dice::CHAIN_CALL],
],
],
];

View file

@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use Friendica\Addon\monolog\src\Factory\Monolog;
use Friendica\Core\Addons\Capabilities\ILoadAddonHooks;
use Psr\Log\LoggerInterface;
return [
ILoadAddonHooks::ADDON_HOOK_STRATEGIES => [
LoggerInterface::class => [
'monolog' => [Monolog::class, []],
],
],
];

View file

@ -0,0 +1,81 @@
<?php
namespace Friendica\Addon\monolog\tests;
use Friendica\Addon\monolog\src\Factory\Monolog;
use Friendica\Test\src\Core\Logger\AbstractLoggerTest;
use Friendica\Test\Util\VFSTrait;
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamFile;
use Psr\Log\LogLevel;
require_once __DIR__ . '/../vendor/autoload.php';
class MonologTest extends AbstractLoggerTest
{
use VFSTrait;
/** @var vfsStreamFile */
protected $logfile;
protected function setUp(): void
{
parent::setUp();
$this->setUpVfsDir();
}
protected function getContent()
{
return $this->logfile->getContent();
}
protected function getInstance($level = LogLevel::DEBUG)
{
$this->logfile = vfsStream::newFile('friendica.log')
->at($this->root);
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($this->logfile->url())->once();
$this->introspection->shouldReceive('addClasses')->with(['Monolog\\']);
$loggerFactory = new Monolog($this->config, $this->introspection, 'test', $level);
return $loggerFactory->create();
}
/**
* Test if a log entry is correctly interpolated
*
* @note - override the base class, because Monolog adds an "array" prefix to the PsrInterpolate when using arrays
*/
public function testPsrInterpolate()
{
$logger = $this->getInstance();
$logger->emergency('A {psr} test', ['psr' => 'working']);
$logger->alert('An {array} test', ['array' => ['it', 'is', 'working']]);
$text = $this->getContent();
self::assertStringContainsString('A working test', $text);
self::assertStringContainsString('An array["it","is","working"] test', $text);
}
/**
* Test a message with an exception
*
* @note - override the base class, because Monolog has an own formatter logic for printing exceptions
*/
public function testExceptionHandling()
{
$e = new \Exception("Test String", 123);
$eFollowUp = new \Exception("FollowUp", 456, $e);
$assertion = '[object] (Exception(code: 456)';
$logger = $this->getInstance();
$logger->alert('test', ['e' => $eFollowUp]);
$text = $this->getContent();
self::assertLogline($text);
self::assertStringContainsString($assertion, $this->getContent());
}
}

View file

@ -26,7 +26,6 @@ return array(
'Monolog\\Formatter\\MongoDBFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php', 'Monolog\\Formatter\\MongoDBFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
'Monolog\\Formatter\\NormalizerFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php', 'Monolog\\Formatter\\NormalizerFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
'Monolog\\Formatter\\ScalarFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php', 'Monolog\\Formatter\\ScalarFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
'Monolog\\Formatter\\SyslogFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php',
'Monolog\\Formatter\\WildfireFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php', 'Monolog\\Formatter\\WildfireFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
'Monolog\\Handler\\AbstractHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php', 'Monolog\\Handler\\AbstractHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
'Monolog\\Handler\\AbstractProcessingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php', 'Monolog\\Handler\\AbstractProcessingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
@ -92,6 +91,7 @@ return array(
'Monolog\\Handler\\SocketHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php', 'Monolog\\Handler\\SocketHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
'Monolog\\Handler\\SqsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php', 'Monolog\\Handler\\SqsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
'Monolog\\Handler\\StreamHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php', 'Monolog\\Handler\\StreamHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
'Monolog\\Handler\\SwiftMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php',
'Monolog\\Handler\\SymfonyMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php', 'Monolog\\Handler\\SymfonyMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
'Monolog\\Handler\\SyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php', 'Monolog\\Handler\\SyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
'Monolog\\Handler\\SyslogUdpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php', 'Monolog\\Handler\\SyslogUdpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
@ -121,12 +121,15 @@ return array(
'Monolog\\SignalHandler' => $vendorDir . '/monolog/monolog/src/Monolog/SignalHandler.php', 'Monolog\\SignalHandler' => $vendorDir . '/monolog/monolog/src/Monolog/SignalHandler.php',
'Monolog\\Test\\TestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/TestCase.php', 'Monolog\\Test\\TestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/TestCase.php',
'Monolog\\Utils' => $vendorDir . '/monolog/monolog/src/Monolog/Utils.php', 'Monolog\\Utils' => $vendorDir . '/monolog/monolog/src/Monolog/Utils.php',
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php', 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php', 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php', 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php', 'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php', 'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php', 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php', 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php', 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
); );

View file

@ -6,6 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
); );

View file

@ -20,7 +20,7 @@ class ComposerStaticInitMonologAddon
public static $prefixDirsPsr4 = array ( public static $prefixDirsPsr4 = array (
'Psr\\Log\\' => 'Psr\\Log\\' =>
array ( array (
0 => __DIR__ . '/..' . '/psr/log/src', 0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
), ),
'Monolog\\' => 'Monolog\\' =>
array ( array (
@ -49,7 +49,6 @@ class ComposerStaticInitMonologAddon
'Monolog\\Formatter\\MongoDBFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php', 'Monolog\\Formatter\\MongoDBFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
'Monolog\\Formatter\\NormalizerFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php', 'Monolog\\Formatter\\NormalizerFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
'Monolog\\Formatter\\ScalarFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php', 'Monolog\\Formatter\\ScalarFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
'Monolog\\Formatter\\SyslogFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php',
'Monolog\\Formatter\\WildfireFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php', 'Monolog\\Formatter\\WildfireFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
'Monolog\\Handler\\AbstractHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php', 'Monolog\\Handler\\AbstractHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
'Monolog\\Handler\\AbstractProcessingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php', 'Monolog\\Handler\\AbstractProcessingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
@ -115,6 +114,7 @@ class ComposerStaticInitMonologAddon
'Monolog\\Handler\\SocketHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php', 'Monolog\\Handler\\SocketHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
'Monolog\\Handler\\SqsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php', 'Monolog\\Handler\\SqsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
'Monolog\\Handler\\StreamHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php', 'Monolog\\Handler\\StreamHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
'Monolog\\Handler\\SwiftMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php',
'Monolog\\Handler\\SymfonyMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php', 'Monolog\\Handler\\SymfonyMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
'Monolog\\Handler\\SyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php', 'Monolog\\Handler\\SyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
'Monolog\\Handler\\SyslogUdpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php', 'Monolog\\Handler\\SyslogUdpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
@ -144,14 +144,17 @@ class ComposerStaticInitMonologAddon
'Monolog\\SignalHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/SignalHandler.php', 'Monolog\\SignalHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/SignalHandler.php',
'Monolog\\Test\\TestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/TestCase.php', 'Monolog\\Test\\TestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/TestCase.php',
'Monolog\\Utils' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Utils.php', 'Monolog\\Utils' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Utils.php',
'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/src/AbstractLogger.php', 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/src/InvalidArgumentException.php', 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/src/LogLevel.php', 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareInterface.php', 'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareTrait.php', 'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerInterface.php', 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerTrait.php', 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/src/NullLogger.php', 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php',
); );
public static function getInitializer(ClassLoader $loader) public static function getInitializer(ClassLoader $loader)

View file

@ -1,28 +1,28 @@
[ [
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.2.0", "version": "2.8.0",
"version_normalized": "3.2.0.0", "version_normalized": "2.8.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Seldaek/monolog.git", "url": "https://github.com/Seldaek/monolog.git",
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81" "reference": "720488632c590286b88b80e62aa3d3d551ad4a50"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/305444bc6fb6c89e490f4b34fa6e979584d7fa81", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50",
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81", "reference": "720488632c590286b88b80e62aa3d3d551ad4a50",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=7.2",
"psr/log": "^2.0 || ^3.0" "psr/log": "^1.0.1 || ^2.0 || ^3.0"
}, },
"provide": { "provide": {
"psr/log-implementation": "3.0.0" "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
}, },
"require-dev": { "require-dev": {
"aws/aws-sdk-php": "^3.0", "aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev", "doctrine/couchdb": "~1.0@dev",
"elasticsearch/elasticsearch": "^7 || ^8", "elasticsearch/elasticsearch": "^7 || ^8",
"ext-json": "*", "ext-json": "*",
@ -31,12 +31,13 @@
"guzzlehttp/psr7": "^2.2", "guzzlehttp/psr7": "^2.2",
"mongodb/mongodb": "^1.8", "mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4 || ^3", "php-amqplib/php-amqplib": "~2.4 || ^3",
"phpstan/phpstan": "^1.4", "phpspec/prophecy": "^1.15",
"phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan": "^0.12.91",
"phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8.5.14",
"phpunit/phpunit": "^9.5.16", "predis/predis": "^1.1 || ^2.0",
"predis/predis": "^1.1", "rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": "^7", "ruflin/elastica": "^7",
"swiftmailer/swiftmailer": "^5.3|^6.0",
"symfony/mailer": "^5.4 || ^6", "symfony/mailer": "^5.4 || ^6",
"symfony/mime": "^5.4 || ^6" "symfony/mime": "^5.4 || ^6"
}, },
@ -56,11 +57,11 @@
"rollbar/rollbar": "Allow sending log messages to Rollbar", "rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server" "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
}, },
"time": "2022-07-24T12:00:55+00:00", "time": "2022-07-24T11:55:47+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.x-dev" "dev-main": "2.x-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
@ -100,33 +101,33 @@
}, },
{ {
"name": "psr/log", "name": "psr/log",
"version": "3.0.0", "version": "1.1.4",
"version_normalized": "3.0.0.0", "version_normalized": "1.1.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/log.git", "url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.0.0" "php": ">=5.3.0"
}, },
"time": "2021-07-14T16:46:02+00:00", "time": "2021-05-03T11:20:27+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.x-dev" "dev-master": "1.1.x-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Psr\\Log\\": "src" "Psr\\Log\\": "Psr/Log/"
} }
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",

View file

@ -1,71 +1,3 @@
### 3.2.0 (2022-07-24)
* Deprecated `CubeHandler` and `PHPConsoleHandler` as both projects are abandoned and those should not be used anymore (#1734)
* Marked `Logger` `@final` as it should not be extended, prefer composition or talk to us if you are missing something
* Added RFC 5424 level (`7` to `0`) support to `Logger::log` and `Logger::addRecord` to increase interoperability (#1723)
* Added `SyslogFormatter` to output syslog-like files which can be consumed by tools like [lnav](https://lnav.org/) (#1689)
* Added support for `__toString` for objects which are not json serializable in `JsonFormatter` (#1733)
* Added `GoogleCloudLoggingFormatter` (#1719)
* Added support for Predis 2.x (#1732)
* Added `AmqpHandler->setExtraAttributes` to allow configuring attributes when using an AMQPExchange (#1724)
* Fixed serialization/unserialization of handlers to make sure private properties are included (#1727)
* Fixed allowInlineLineBreaks in LineFormatter causing issues with windows paths containing `\n` or `\r` sequences (#1720)
* Fixed max normalization depth not being taken into account when formatting exceptions with a deep chain of previous exceptions (#1726)
* Fixed PHP 8.2 deprecation warnings (#1722)
* Fixed rare race condition or filesystem issue where StreamHandler is unable to create the directory the log should go into yet it exists already (#1678)
### 3.1.0 (2022-06-09)
* Added `$datetime` parameter to `Logger::addRecord` as low level API to allow logging into the past or future (#1682)
* Added `Logger::useLoggingLoopDetection` to allow disabling cyclic logging detection in concurrent frameworks (#1681)
* Fixed handling of fatal errors if callPrevious is disabled in ErrorHandler (#1670)
* Fixed interop issue by removing the need for a return type in ProcessorInterface (#1680)
* Marked the reusable `Monolog\Test\TestCase` class as `@internal` to make sure PHPStorm does not show it above PHPUnit, you may still use it to test your own handlers/etc though (#1677)
* Fixed RotatingFileHandler issue when the date format contained slashes (#1671)
### 3.0.0 (2022-05-10)
Changes from RC1
- The `Monolog\LevelName` enum does not exist anymore, use `Monolog\Level->getName()` instead.
### 3.0.0-RC1 (2022-05-08)
This is mostly a cleanup release offering stronger type guarantees for integrators with the
array->object/enum changes, but there is no big new feature for end users.
See [UPGRADE notes](UPGRADE.md#300) for details on all breaking changes especially if you are extending/implementing Monolog classes/interfaces.
Noteworthy BC Breaks:
- The minimum supported PHP version is now `8.1.0`.
- Log records have been converted from an array to a [`Monolog\LogRecord` object](src/Monolog/LogRecord.php)
with public (and mostly readonly) properties. e.g. instead of doing
`$record['context']` use `$record->context`.
In formatters or handlers if you rather need an array to work with you can use `$record->toArray()`
to get back a Monolog 1/2 style record array. This will contain the enum values instead of enum cases
in the `level` and `level_name` keys to be more backwards compatible and use simpler data types.
- `FormatterInterface`, `HandlerInterface`, `ProcessorInterface`, etc. changed to contain `LogRecord $record`
instead of `array $record` parameter types. If you want to support multiple Monolog versions this should
be possible by type-hinting nothing, or `array|LogRecord` if you support PHP 8.0+. You can then code
against the $record using Monolog 2 style as LogRecord implements ArrayAccess for BC.
The interfaces do not require a `LogRecord` return type even where it would be applicable, but if you only
support Monolog 3 in integration code I would recommend you use `LogRecord` return types wherever fitting
to ensure forward compatibility as it may be added in Monolog 4.
- Log levels are now enums [`Monolog\Level`](src/Monolog/Level.php) and [`Monolog\LevelName`](src/Monolog/LevelName.php)
- Removed deprecated SwiftMailerHandler, migrate to SymfonyMailerHandler instead.
- `ResettableInterface::reset()` now requires a void return type.
- All properties have had types added, which may require you to do so as well if you extended
a Monolog class and declared the same property.
New deprecations:
- `Logger::DEBUG`, `Logger::ERROR`, etc. are now deprecated in favor of the `Monolog\Level` enum.
e.g. instead of `Logger::WARNING` use `Level::Warning` if you need to pass the enum case
to Monolog or one of its handlers, or `Level::Warning->value` if you need the integer
value equal to what `Logger::WARNING` was giving you.
- `Logger::getLevelName()` is now deprecated.
### 2.8.0 (2022-07-24) ### 2.8.0 (2022-07-24)
* Deprecated `CubeHandler` and `PHPConsoleHandler` as both projects are abandoned and those should not be used anymore (#1734) * Deprecated `CubeHandler` and `PHPConsoleHandler` as both projects are abandoned and those should not be used anymore (#1734)

View file

@ -3,8 +3,6 @@
[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) [![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) [![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
> ⚠ This is the **documentation for Monolog 3.x**, if you are using older releases
> see the documentation for [Monolog 2.x](https://github.com/Seldaek/monolog/blob/2.x/README.md) or [Monolog 1.x](https://github.com/Seldaek/monolog/blob/1.x/README.md) ⚠
Monolog sends your logs to files, sockets, inboxes, databases and various Monolog sends your logs to files, sockets, inboxes, databases and various
web services. See the complete list of handlers below. Special handlers web services. See the complete list of handlers below. Special handlers
@ -30,13 +28,12 @@ $ composer require monolog/monolog
```php ```php
<?php <?php
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Handler\StreamHandler; use Monolog\Handler\StreamHandler;
// create a log channel // create a log channel
$log = new Logger('name'); $log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Level::Warning)); $log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
// add records to the log // add records to the log
$log->warning('Foo'); $log->warning('Foo');
@ -67,13 +64,11 @@ can also add your own there if you publish one.
### Requirements ### Requirements
- Monolog `^3.0` works with PHP 8.1 or above. - Monolog `^2.0` works with PHP 7.2 or above, use Monolog `^1.25` for PHP 5.3+ support.
- Monolog `^2.5` works with PHP 7.2 or above.
- Monolog `^1.25` works with PHP 5.3 up to 8.1, but is not very maintained anymore and will not receive PHP support fixes anymore.
### Support ### Support
Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 or 3 where possible to benefit from all the latest features and fixes. Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 where possible to benefit from all the latest features and fixes.
### Submitting bugs and feature requests ### Submitting bugs and feature requests

View file

@ -13,12 +13,12 @@
} }
], ],
"require": { "require": {
"php": ">=8.1", "php": ">=7.2",
"psr/log": "^2.0 || ^3.0" "psr/log": "^1.0.1 || ^2.0 || ^3.0"
}, },
"require-dev": { "require-dev": {
"ext-json": "*", "ext-json": "*",
"aws/aws-sdk-php": "^3.0", "aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev", "doctrine/couchdb": "~1.0@dev",
"elasticsearch/elasticsearch": "^7 || ^8", "elasticsearch/elasticsearch": "^7 || ^8",
"graylog2/gelf-php": "^1.4.2", "graylog2/gelf-php": "^1.4.2",
@ -26,12 +26,13 @@
"guzzlehttp/psr7": "^2.2", "guzzlehttp/psr7": "^2.2",
"mongodb/mongodb": "^1.8", "mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4 || ^3", "php-amqplib/php-amqplib": "~2.4 || ^3",
"phpstan/phpstan": "^1.4", "phpspec/prophecy": "^1.15",
"phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan": "^0.12.91",
"phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8.5.14",
"phpunit/phpunit": "^9.5.16", "predis/predis": "^1.1 || ^2.0",
"predis/predis": "^1.1", "rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": "^7", "ruflin/elastica": "^7",
"swiftmailer/swiftmailer": "^5.3|^6.0",
"symfony/mailer": "^5.4 || ^6", "symfony/mailer": "^5.4 || ^6",
"symfony/mime": "^5.4 || ^6" "symfony/mime": "^5.4 || ^6"
}, },
@ -58,11 +59,11 @@
"psr-4": {"Monolog\\": "tests/Monolog"} "psr-4": {"Monolog\\": "tests/Monolog"}
}, },
"provide": { "provide": {
"psr/log-implementation": "3.0.0" "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.x-dev" "dev-main": "2.x-dev"
} }
}, },
"scripts": { "scripts": {
@ -72,6 +73,9 @@
"config": { "config": {
"lock": false, "lock": false,
"sort-packages": true, "sort-packages": true,
"platform-check": false "platform-check": false,
"allow-plugins": {
"composer/package-versions-deprecated": true
}
} }
} }

View file

@ -22,15 +22,25 @@ namespace Monolog\Attribute;
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsMonologProcessor class AsMonologProcessor
{ {
/** @var string|null */
public $channel = null;
/** @var string|null */
public $handler = null;
/** @var string|null */
public $method = null;
/** /**
* @param string|null $channel The logging channel the processor should be pushed to. * @param string|null $channel The logging channel the processor should be pushed to.
* @param string|null $handler The handler the processor should be pushed to. * @param string|null $handler The handler the processor should be pushed to.
* @param string|null $method The method that processes the records (if the attribute is used at the class level). * @param string|null $method The method that processes the records (if the attribute is used at the class level).
*/ */
public function __construct( public function __construct(
public readonly ?string $channel = null, ?string $channel = null,
public readonly ?string $handler = null, ?string $handler = null,
public readonly ?string $method = null ?string $method = null
) { ) {
$this->channel = $channel;
$this->handler = $handler;
$this->method = $method;
} }
} }

View file

@ -21,7 +21,10 @@ use DateTimeZone;
*/ */
class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable
{ {
private bool $useMicroseconds; /**
* @var bool
*/
private $useMicroseconds;
public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null) public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null)
{ {

View file

@ -11,7 +11,6 @@
namespace Monolog; namespace Monolog;
use Closure;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
@ -26,33 +25,35 @@ use Psr\Log\LogLevel;
*/ */
class ErrorHandler class ErrorHandler
{ {
private Closure|null $previousExceptionHandler = null; /** @var LoggerInterface */
private $logger;
/** @var ?callable */
private $previousExceptionHandler = null;
/** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */ /** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
private array $uncaughtExceptionLevelMap = []; private $uncaughtExceptionLevelMap = [];
/** @var Closure|true|null */
private Closure|bool|null $previousErrorHandler = null;
/** @var callable|true|null */
private $previousErrorHandler = null;
/** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */ /** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
private array $errorLevelMap = []; private $errorLevelMap = [];
/** @var bool */
private bool $handleOnlyReportedErrors = true; private $handleOnlyReportedErrors = true;
private bool $hasFatalErrorHandler = false;
private string $fatalLevel = LogLevel::ALERT;
private string|null $reservedMemory = null;
/** @var bool */
private $hasFatalErrorHandler = false;
/** @var LogLevel::* */
private $fatalLevel = LogLevel::ALERT;
/** @var ?string */
private $reservedMemory = null;
/** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */ /** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */
private array|null $lastFatalData = null; private $lastFatalData = null;
/** @var int[] */
private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
private const FATAL_ERRORS = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]; public function __construct(LoggerInterface $logger)
{
public function __construct( $this->logger = $logger;
private LoggerInterface $logger
) {
} }
/** /**
@ -60,6 +61,7 @@ class ErrorHandler
* *
* By default it will handle errors, exceptions and fatal errors * By default it will handle errors, exceptions and fatal errors
* *
* @param LoggerInterface $logger
* @param array<int, LogLevel::*>|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling * @param array<int, LogLevel::*>|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
* @param array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling * @param array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
* @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling * @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
@ -97,8 +99,8 @@ class ErrorHandler
$this->uncaughtExceptionLevelMap[$class] = $level; $this->uncaughtExceptionLevelMap[$class] = $level;
} }
} }
if ($callPrevious && null !== $prev) { if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev(...); $this->previousExceptionHandler = $prev;
} }
return $this; return $this;
@ -110,10 +112,10 @@ class ErrorHandler
*/ */
public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
{ {
$prev = set_error_handler($this->handleError(...), $errorTypes); $prev = set_error_handler([$this, 'handleError'], $errorTypes);
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
if ($callPrevious) { if ($callPrevious) {
$this->previousErrorHandler = $prev !== null ? $prev(...) : true; $this->previousErrorHandler = $prev ?: true;
} else { } else {
$this->previousErrorHandler = null; $this->previousErrorHandler = null;
} }
@ -129,7 +131,7 @@ class ErrorHandler
*/ */
public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
{ {
register_shutdown_function($this->handleFatalError(...)); register_shutdown_function([$this, 'handleFatalError']);
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
$this->fatalLevel = null === $level ? LogLevel::ALERT : $level; $this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
@ -173,7 +175,10 @@ class ErrorHandler
]; ];
} }
private function handleException(\Throwable $e): never /**
* @phpstan-return never
*/
private function handleException(\Throwable $e): void
{ {
$level = LogLevel::ERROR; $level = LogLevel::ERROR;
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) { foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
@ -189,25 +194,30 @@ class ErrorHandler
['exception' => $e] ['exception' => $e]
); );
if (null !== $this->previousExceptionHandler) { if ($this->previousExceptionHandler) {
($this->previousExceptionHandler)($e); ($this->previousExceptionHandler)($e);
} }
if (!headers_sent() && !(bool) ini_get('display_errors')) { if (!headers_sent() && !ini_get('display_errors')) {
http_response_code(500); http_response_code(500);
} }
exit(255); exit(255);
} }
private function handleError(int $code, string $message, string $file = '', int $line = 0): bool /**
* @private
*
* @param mixed[] $context
*/
public function handleError(int $code, string $message, string $file = '', int $line = 0, ?array $context = []): bool
{ {
if ($this->handleOnlyReportedErrors && 0 === (error_reporting() & $code)) { if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) {
return false; return false;
} }
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
if (!$this->hasFatalErrorHandler || !in_array($code, self::FATAL_ERRORS, true)) { if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) {
$level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL; $level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL;
$this->logger->log($level, self::codeToString($code).': '.$message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]); $this->logger->log($level, self::codeToString($code).': '.$message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]);
} else { } else {
@ -218,9 +228,8 @@ class ErrorHandler
if ($this->previousErrorHandler === true) { if ($this->previousErrorHandler === true) {
return false; return false;
} } elseif ($this->previousErrorHandler) {
if ($this->previousErrorHandler instanceof Closure) { return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context);
return (bool) ($this->previousErrorHandler)($code, $message, $file, $line);
} }
return true; return true;
@ -238,7 +247,8 @@ class ErrorHandler
} else { } else {
$lastError = error_get_last(); $lastError = error_get_last();
} }
if (is_array($lastError) && in_array($lastError['type'], self::FATAL_ERRORS, true)) {
if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) {
$trace = $lastError['trace'] ?? null; $trace = $lastError['trace'] ?? null;
$this->logger->log( $this->logger->log(
$this->fatalLevel, $this->fatalLevel,
@ -254,25 +264,44 @@ class ErrorHandler
} }
} }
private static function codeToString(int $code): string /**
* @param int $code
*/
private static function codeToString($code): string
{ {
return match ($code) { switch ($code) {
E_ERROR => 'E_ERROR', case E_ERROR:
E_WARNING => 'E_WARNING', return 'E_ERROR';
E_PARSE => 'E_PARSE', case E_WARNING:
E_NOTICE => 'E_NOTICE', return 'E_WARNING';
E_CORE_ERROR => 'E_CORE_ERROR', case E_PARSE:
E_CORE_WARNING => 'E_CORE_WARNING', return 'E_PARSE';
E_COMPILE_ERROR => 'E_COMPILE_ERROR', case E_NOTICE:
E_COMPILE_WARNING => 'E_COMPILE_WARNING', return 'E_NOTICE';
E_USER_ERROR => 'E_USER_ERROR', case E_CORE_ERROR:
E_USER_WARNING => 'E_USER_WARNING', return 'E_CORE_ERROR';
E_USER_NOTICE => 'E_USER_NOTICE', case E_CORE_WARNING:
E_STRICT => 'E_STRICT', return 'E_CORE_WARNING';
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', case E_COMPILE_ERROR:
E_DEPRECATED => 'E_DEPRECATED', return 'E_COMPILE_ERROR';
E_USER_DEPRECATED => 'E_USER_DEPRECATED', case E_COMPILE_WARNING:
default => 'Unknown PHP error', return 'E_COMPILE_WARNING';
}; case E_USER_ERROR:
return 'E_USER_ERROR';
case E_USER_WARNING:
return 'E_USER_WARNING';
case E_USER_NOTICE:
return 'E_USER_NOTICE';
case E_STRICT:
return 'E_STRICT';
case E_RECOVERABLE_ERROR:
return 'E_RECOVERABLE_ERROR';
case E_DEPRECATED:
return 'E_DEPRECATED';
case E_USER_DEPRECATED:
return 'E_USER_DEPRECATED';
}
return 'Unknown PHP error';
} }
} }

View file

@ -11,8 +11,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Formats a log message according to the ChromePHP array format * Formats a log message according to the ChromePHP array format
@ -24,55 +23,52 @@ class ChromePHPFormatter implements FormatterInterface
/** /**
* Translates Monolog log levels to Wildfire levels. * Translates Monolog log levels to Wildfire levels.
* *
* @return 'log'|'info'|'warn'|'error' * @var array<int, 'log'|'info'|'warn'|'error'>
*/ */
private function toWildfireLevel(Level $level): string private $logLevels = [
{ Logger::DEBUG => 'log',
return match ($level) { Logger::INFO => 'info',
Level::Debug => 'log', Logger::NOTICE => 'info',
Level::Info => 'info', Logger::WARNING => 'warn',
Level::Notice => 'info', Logger::ERROR => 'error',
Level::Warning => 'warn', Logger::CRITICAL => 'error',
Level::Error => 'error', Logger::ALERT => 'error',
Level::Critical => 'error', Logger::EMERGENCY => 'error',
Level::Alert => 'error', ];
Level::Emergency => 'error',
};
}
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record) public function format(array $record)
{ {
// Retrieve the line and file if set and remove them from the formatted extra // Retrieve the line and file if set and remove them from the formatted extra
$backtrace = 'unknown'; $backtrace = 'unknown';
if (isset($record->extra['file'], $record->extra['line'])) { if (isset($record['extra']['file'], $record['extra']['line'])) {
$backtrace = $record->extra['file'].' : '.$record->extra['line']; $backtrace = $record['extra']['file'].' : '.$record['extra']['line'];
unset($record->extra['file'], $record->extra['line']); unset($record['extra']['file'], $record['extra']['line']);
} }
$message = ['message' => $record->message]; $message = ['message' => $record['message']];
if (\count($record->context) > 0) { if ($record['context']) {
$message['context'] = $record->context; $message['context'] = $record['context'];
} }
if (\count($record->extra) > 0) { if ($record['extra']) {
$message['extra'] = $record->extra; $message['extra'] = $record['extra'];
} }
if (count($message) === 1) { if (count($message) === 1) {
$message = reset($message); $message = reset($message);
} }
return [ return [
$record->channel, $record['channel'],
$message, $message,
$backtrace, $backtrace,
$this->toWildfireLevel($record->level), $this->logLevels[$record['level']],
]; ];
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function formatBatch(array $records) public function formatBatch(array $records)
{ {

View file

@ -12,24 +12,25 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Elastica\Document; use Elastica\Document;
use Monolog\LogRecord;
/** /**
* Format a log message into an Elastica Document * Format a log message into an Elastica Document
* *
* @author Jelle Vink <jelle.vink@gmail.com> * @author Jelle Vink <jelle.vink@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class ElasticaFormatter extends NormalizerFormatter class ElasticaFormatter extends NormalizerFormatter
{ {
/** /**
* @var string Elastic search index name * @var string Elastic search index name
*/ */
protected string $index; protected $index;
/** /**
* @var string|null Elastic search document type * @var ?string Elastic search document type
*/ */
protected string|null $type; protected $type;
/** /**
* @param string $index Elastic Search index name * @param string $index Elastic Search index name
@ -45,9 +46,9 @@ class ElasticaFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record) public function format(array $record)
{ {
$record = parent::format($record); $record = parent::format($record);
@ -71,13 +72,14 @@ class ElasticaFormatter extends NormalizerFormatter
/** /**
* Convert a log message into an Elastica Document * Convert a log message into an Elastica Document
* *
* @param mixed[] $record * @phpstan-param Record $record
*/ */
protected function getDocument(array $record): Document protected function getDocument(array $record): Document
{ {
$document = new Document(); $document = new Document();
$document->setData($record); $document->setData($record);
if (method_exists($document, 'setType')) { if (method_exists($document, 'setType')) {
/** @phpstan-ignore-next-line */
$document->setType($this->type); $document->setType($this->type);
} }
$document->setIndex($this->index); $document->setIndex($this->index);

View file

@ -12,7 +12,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use DateTimeInterface; use DateTimeInterface;
use Monolog\LogRecord;
/** /**
* Format a log message into an Elasticsearch record * Format a log message into an Elasticsearch record
@ -24,12 +23,12 @@ class ElasticsearchFormatter extends NormalizerFormatter
/** /**
* @var string Elasticsearch index name * @var string Elasticsearch index name
*/ */
protected string $index; protected $index;
/** /**
* @var string Elasticsearch record type * @var string Elasticsearch record type
*/ */
protected string $type; protected $type;
/** /**
* @param string $index Elasticsearch index name * @param string $index Elasticsearch index name
@ -45,9 +44,9 @@ class ElasticsearchFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record) public function format(array $record)
{ {
$record = parent::format($record); $record = parent::format($record);
@ -56,6 +55,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
/** /**
* Getter index * Getter index
*
* @return string
*/ */
public function getIndex(): string public function getIndex(): string
{ {
@ -64,6 +65,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
/** /**
* Getter type * Getter type
*
* @return string
*/ */
public function getType(): string public function getType(): string
{ {

View file

@ -11,8 +11,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\LogRecord;
/** /**
* formats the record to be used in the FlowdockHandler * formats the record to be used in the FlowdockHandler
* *
@ -20,9 +18,15 @@ use Monolog\LogRecord;
*/ */
class FlowdockFormatter implements FormatterInterface class FlowdockFormatter implements FormatterInterface
{ {
private string $source; /**
* @var string
*/
private $source;
private string $sourceEmail; /**
* @var string
*/
private $sourceEmail;
public function __construct(string $source, string $sourceEmail) public function __construct(string $source, string $sourceEmail)
{ {
@ -31,41 +35,43 @@ class FlowdockFormatter implements FormatterInterface
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @return mixed[] * @return mixed[]
*/ */
public function format(LogRecord $record): array public function format(array $record): array
{ {
$tags = [ $tags = [
'#logs', '#logs',
'#' . $record->level->toPsrLogLevel(), '#' . strtolower($record['level_name']),
'#' . $record->channel, '#' . $record['channel'],
]; ];
foreach ($record->extra as $value) { foreach ($record['extra'] as $value) {
$tags[] = '#' . $value; $tags[] = '#' . $value;
} }
$subject = sprintf( $subject = sprintf(
'in %s: %s - %s', 'in %s: %s - %s',
$this->source, $this->source,
$record->level->getName(), $record['level_name'],
$this->getShortMessage($record->message) $this->getShortMessage($record['message'])
); );
return [ $record['flowdock'] = [
'source' => $this->source, 'source' => $this->source,
'from_address' => $this->sourceEmail, 'from_address' => $this->sourceEmail,
'subject' => $subject, 'subject' => $subject,
'content' => $record->message, 'content' => $record['message'],
'tags' => $tags, 'tags' => $tags,
'project' => $this->source, 'project' => $this->source,
]; ];
return $record;
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @return mixed[][] * @return mixed[][]
*/ */

View file

@ -12,7 +12,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Class FluentdFormatter * Class FluentdFormatter
@ -40,7 +39,7 @@ class FluentdFormatter implements FormatterInterface
/** /**
* @var bool $levelTag should message level be a part of the fluentd tag * @var bool $levelTag should message level be a part of the fluentd tag
*/ */
protected bool $levelTag = false; protected $levelTag = false;
public function __construct(bool $levelTag = false) public function __construct(bool $levelTag = false)
{ {
@ -56,25 +55,25 @@ class FluentdFormatter implements FormatterInterface
return $this->levelTag; return $this->levelTag;
} }
public function format(LogRecord $record): string public function format(array $record): string
{ {
$tag = $record->channel; $tag = $record['channel'];
if ($this->levelTag) { if ($this->levelTag) {
$tag .= '.' . $record->level->toPsrLogLevel(); $tag .= '.' . strtolower($record['level_name']);
} }
$message = [ $message = [
'message' => $record->message, 'message' => $record['message'],
'context' => $record->context, 'context' => $record['context'],
'extra' => $record->extra, 'extra' => $record['extra'],
]; ];
if (!$this->levelTag) { if (!$this->levelTag) {
$message['level'] = $record->level->value; $message['level'] = $record['level'];
$message['level_name'] = $record->level->getName(); $message['level_name'] = $record['level_name'];
} }
return Utils::jsonEncode([$tag, $record->datetime->getTimestamp(), $message]); return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]);
} }
public function formatBatch(array $records): string public function formatBatch(array $records): string

View file

@ -11,28 +11,32 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\LogRecord;
/** /**
* Interface for formatters * Interface for formatters
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
interface FormatterInterface interface FormatterInterface
{ {
/** /**
* Formats a log record. * Formats a log record.
* *
* @param LogRecord $record A record to format * @param array $record A record to format
* @return mixed The formatted record * @return mixed The formatted record
*
* @phpstan-param Record $record
*/ */
public function format(LogRecord $record); public function format(array $record);
/** /**
* Formats a set of log records. * Formats a set of log records.
* *
* @param array<LogRecord> $records A set of records to format * @param array $records A set of records to format
* @return mixed The formatted set of records * @return mixed The formatted set of records
*
* @phpstan-param Record[] $records
*/ */
public function formatBatch(array $records); public function formatBatch(array $records);
} }

View file

@ -11,16 +11,17 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Level; use Monolog\Logger;
use Gelf\Message; use Gelf\Message;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Serializes a log message to GELF * Serializes a log message to GELF
* @see http://docs.graylog.org/en/latest/pages/gelf.html * @see http://docs.graylog.org/en/latest/pages/gelf.html
* *
* @author Matt Lehner <mlehner@gmail.com> * @author Matt Lehner <mlehner@gmail.com>
*
* @phpstan-import-type Level from \Monolog\Logger
*/ */
class GelfMessageFormatter extends NormalizerFormatter class GelfMessageFormatter extends NormalizerFormatter
{ {
@ -29,39 +30,40 @@ class GelfMessageFormatter extends NormalizerFormatter
/** /**
* @var string the name of the system for the Gelf log message * @var string the name of the system for the Gelf log message
*/ */
protected string $systemName; protected $systemName;
/** /**
* @var string a prefix for 'extra' fields from the Monolog record (optional) * @var string a prefix for 'extra' fields from the Monolog record (optional)
*/ */
protected string $extraPrefix; protected $extraPrefix;
/** /**
* @var string a prefix for 'context' fields from the Monolog record (optional) * @var string a prefix for 'context' fields from the Monolog record (optional)
*/ */
protected string $contextPrefix; protected $contextPrefix;
/** /**
* @var int max length per field * @var int max length per field
*/ */
protected int $maxLength; protected $maxLength;
/** /**
* Translates Monolog log levels to Graylog2 log priorities. * Translates Monolog log levels to Graylog2 log priorities.
*
* @var array<int, int>
*
* @phpstan-var array<Level, int>
*/ */
private function getGraylog2Priority(Level $level): int private $logLevels = [
{ Logger::DEBUG => 7,
return match ($level) { Logger::INFO => 6,
Level::Debug => 7, Logger::NOTICE => 5,
Level::Info => 6, Logger::WARNING => 4,
Level::Notice => 5, Logger::ERROR => 3,
Level::Warning => 4, Logger::CRITICAL => 2,
Level::Error => 3, Logger::ALERT => 1,
Level::Critical => 2, Logger::EMERGENCY => 0,
Level::Alert => 1, ];
Level::Emergency => 0,
};
}
public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null) public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null)
{ {
@ -71,51 +73,55 @@ class GelfMessageFormatter extends NormalizerFormatter
parent::__construct('U.u'); parent::__construct('U.u');
$this->systemName = (null === $systemName || $systemName === '') ? (string) gethostname() : $systemName; $this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName;
$this->extraPrefix = null === $extraPrefix ? '' : $extraPrefix; $this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix;
$this->contextPrefix = $contextPrefix; $this->contextPrefix = $contextPrefix;
$this->maxLength = null === $maxLength ? self::DEFAULT_MAX_LENGTH : $maxLength; $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record): Message public function format(array $record): Message
{ {
$context = $extra = []; $context = $extra = [];
if (isset($record->context)) { if (isset($record['context'])) {
/** @var mixed[] $context */ /** @var mixed[] $context */
$context = parent::normalize($record->context); $context = parent::normalize($record['context']);
} }
if (isset($record->extra)) { if (isset($record['extra'])) {
/** @var mixed[] $extra */ /** @var mixed[] $extra */
$extra = parent::normalize($record->extra); $extra = parent::normalize($record['extra']);
}
if (!isset($record['datetime'], $record['message'], $record['level'])) {
throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given');
} }
$message = new Message(); $message = new Message();
$message $message
->setTimestamp($record->datetime) ->setTimestamp($record['datetime'])
->setShortMessage($record->message) ->setShortMessage((string) $record['message'])
->setHost($this->systemName) ->setHost($this->systemName)
->setLevel($this->getGraylog2Priority($record->level)); ->setLevel($this->logLevels[$record['level']]);
// message length + system name length + 200 for padding / metadata // message length + system name length + 200 for padding / metadata
$len = 200 + strlen($record->message) + strlen($this->systemName); $len = 200 + strlen((string) $record['message']) + strlen($this->systemName);
if ($len > $this->maxLength) { if ($len > $this->maxLength) {
$message->setShortMessage(Utils::substr($record->message, 0, $this->maxLength)); $message->setShortMessage(Utils::substr($record['message'], 0, $this->maxLength));
} }
if (isset($record->channel)) { if (isset($record['channel'])) {
$message->setAdditional('facility', $record->channel); $message->setFacility($record['channel']);
} }
if (isset($extra['line'])) { if (isset($extra['line'])) {
$message->setAdditional('line', $extra['line']); $message->setLine($extra['line']);
unset($extra['line']); unset($extra['line']);
} }
if (isset($extra['file'])) { if (isset($extra['file'])) {
$message->setAdditional('file', $extra['file']); $message->setFile($extra['file']);
unset($extra['file']); unset($extra['file']);
} }
@ -141,10 +147,11 @@ class GelfMessageFormatter extends NormalizerFormatter
$message->setAdditional($this->contextPrefix . $key, $val); $message->setAdditional($this->contextPrefix . $key, $val);
} }
if (!$message->hasAdditional('file') && isset($context['exception']['file'])) { /** @phpstan-ignore-next-line */
if (1 === preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) { if (null === $message->getFile() && isset($context['exception']['file'])) {
$message->setAdditional('file', $matches[1]); if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) {
$message->setAdditional('line', $matches[2]); $message->setFile($matches[1]);
$message->setLine($matches[2]);
} }
} }

View file

@ -23,17 +23,17 @@ use Monolog\LogRecord;
*/ */
final class GoogleCloudLoggingFormatter extends JsonFormatter final class GoogleCloudLoggingFormatter extends JsonFormatter
{ {
protected function normalizeRecord(LogRecord $record): array /** {@inheritdoc} **/
public function format(array $record): string
{ {
$normalized = parent::normalizeRecord($record);
// Re-key level for GCP logging // Re-key level for GCP logging
$normalized['severity'] = $normalized['level_name']; $record['severity'] = $record['level_name'];
$normalized['timestamp'] = $record->datetime->format(DateTimeInterface::RFC3339_EXTENDED); $record['timestamp'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED);
// Remove keys that are not used by GCP // Remove keys that are not used by GCP
unset($normalized['level'], $normalized['level_name'], $normalized['datetime']); unset($record['level'], $record['level_name'], $record['datetime']);
return $normalized; return parent::format($record);
} }
} }

View file

@ -11,9 +11,8 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Formats incoming records into an HTML table * Formats incoming records into an HTML table
@ -26,20 +25,19 @@ class HtmlFormatter extends NormalizerFormatter
{ {
/** /**
* Translates Monolog log levels to html color priorities. * Translates Monolog log levels to html color priorities.
*
* @var array<int, string>
*/ */
protected function getLevelColor(Level $level): string protected $logLevels = [
{ Logger::DEBUG => '#CCCCCC',
return match ($level) { Logger::INFO => '#28A745',
Level::Debug => '#CCCCCC', Logger::NOTICE => '#17A2B8',
Level::Info => '#28A745', Logger::WARNING => '#FFC107',
Level::Notice => '#17A2B8', Logger::ERROR => '#FD7E14',
Level::Warning => '#FFC107', Logger::CRITICAL => '#DC3545',
Level::Error => '#FD7E14', Logger::ALERT => '#821722',
Level::Critical => '#DC3545', Logger::EMERGENCY => '#000000',
Level::Alert => '#821722', ];
Level::Emergency => '#000000',
};
}
/** /**
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
@ -70,12 +68,14 @@ class HtmlFormatter extends NormalizerFormatter
* Create a HTML h1 tag * Create a HTML h1 tag
* *
* @param string $title Text to be in the h1 * @param string $title Text to be in the h1
* @param int $level Error level
* @return string
*/ */
protected function addTitle(string $title, Level $level): string protected function addTitle(string $title, int $level): string
{ {
$title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');
return '<h1 style="background: '.$this->getLevelColor($level).';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>'; return '<h1 style="background: '.$this->logLevels[$level].';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>';
} }
/** /**
@ -83,25 +83,25 @@ class HtmlFormatter extends NormalizerFormatter
* *
* @return string The formatted record * @return string The formatted record
*/ */
public function format(LogRecord $record): string public function format(array $record): string
{ {
$output = $this->addTitle($record->level->getName(), $record->level); $output = $this->addTitle($record['level_name'], $record['level']);
$output .= '<table cellspacing="1" width="100%" class="monolog-output">'; $output .= '<table cellspacing="1" width="100%" class="monolog-output">';
$output .= $this->addRow('Message', $record->message); $output .= $this->addRow('Message', (string) $record['message']);
$output .= $this->addRow('Time', $this->formatDate($record->datetime)); $output .= $this->addRow('Time', $this->formatDate($record['datetime']));
$output .= $this->addRow('Channel', $record->channel); $output .= $this->addRow('Channel', $record['channel']);
if (\count($record->context) > 0) { if ($record['context']) {
$embeddedTable = '<table cellspacing="1" width="100%">'; $embeddedTable = '<table cellspacing="1" width="100%">';
foreach ($record->context as $key => $value) { foreach ($record['context'] as $key => $value) {
$embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
} }
$embeddedTable .= '</table>'; $embeddedTable .= '</table>';
$output .= $this->addRow('Context', $embeddedTable, false); $output .= $this->addRow('Context', $embeddedTable, false);
} }
if (\count($record->extra) > 0) { if ($record['extra']) {
$embeddedTable = '<table cellspacing="1" width="100%">'; $embeddedTable = '<table cellspacing="1" width="100%">';
foreach ($record->extra as $key => $value) { foreach ($record['extra'] as $key => $value) {
$embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
} }
$embeddedTable .= '</table>'; $embeddedTable .= '</table>';

View file

@ -11,9 +11,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Stringable;
use Throwable; use Throwable;
use Monolog\LogRecord;
/** /**
* Encodes whatever record data is passed to it as json * Encodes whatever record data is passed to it as json
@ -21,6 +19,8 @@ use Monolog\LogRecord;
* This can be useful to log to databases or remote APIs * This can be useful to log to databases or remote APIs
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class JsonFormatter extends NormalizerFormatter class JsonFormatter extends NormalizerFormatter
{ {
@ -28,13 +28,13 @@ class JsonFormatter extends NormalizerFormatter
public const BATCH_MODE_NEWLINES = 2; public const BATCH_MODE_NEWLINES = 2;
/** @var self::BATCH_MODE_* */ /** @var self::BATCH_MODE_* */
protected int $batchMode; protected $batchMode;
/** @var bool */
protected bool $appendNewline; protected $appendNewline;
/** @var bool */
protected bool $ignoreEmptyContextAndExtra; protected $ignoreEmptyContextAndExtra;
/** @var bool */
protected bool $includeStacktraces = false; protected $includeStacktraces = false;
/** /**
* @param self::BATCH_MODE_* $batchMode * @param self::BATCH_MODE_* $batchMode
@ -70,11 +70,11 @@ class JsonFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record): string public function format(array $record): string
{ {
$normalized = parent::format($record); $normalized = $this->normalize($record);
if (isset($normalized['context']) && $normalized['context'] === []) { if (isset($normalized['context']) && $normalized['context'] === []) {
if ($this->ignoreEmptyContextAndExtra) { if ($this->ignoreEmptyContextAndExtra) {
@ -95,16 +95,23 @@ class JsonFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function formatBatch(array $records): string public function formatBatch(array $records): string
{ {
return match ($this->batchMode) { switch ($this->batchMode) {
static::BATCH_MODE_NEWLINES => $this->formatBatchNewlines($records), case static::BATCH_MODE_NEWLINES:
default => $this->formatBatchJson($records), return $this->formatBatchNewlines($records);
};
case static::BATCH_MODE_JSON:
default:
return $this->formatBatchJson($records);
}
} }
/**
* @return self
*/
public function includeStacktraces(bool $include = true): self public function includeStacktraces(bool $include = true): self
{ {
$this->includeStacktraces = $include; $this->includeStacktraces = $include;
@ -115,7 +122,7 @@ class JsonFormatter extends NormalizerFormatter
/** /**
* Return a JSON-encoded array of records. * Return a JSON-encoded array of records.
* *
* @phpstan-param LogRecord[] $records * @phpstan-param Record[] $records
*/ */
protected function formatBatchJson(array $records): string protected function formatBatchJson(array $records): string
{ {
@ -126,24 +133,30 @@ class JsonFormatter extends NormalizerFormatter
* Use new lines to separate records instead of a * Use new lines to separate records instead of a
* JSON-encoded array. * JSON-encoded array.
* *
* @phpstan-param LogRecord[] $records * @phpstan-param Record[] $records
*/ */
protected function formatBatchNewlines(array $records): string protected function formatBatchNewlines(array $records): string
{ {
$instance = $this;
$oldNewline = $this->appendNewline; $oldNewline = $this->appendNewline;
$this->appendNewline = false; $this->appendNewline = false;
$formatted = array_map(fn (LogRecord $record) => $this->format($record), $records); array_walk($records, function (&$value, $key) use ($instance) {
$value = $instance->format($value);
});
$this->appendNewline = $oldNewline; $this->appendNewline = $oldNewline;
return implode("\n", $formatted); return implode("\n", $records);
} }
/** /**
* Normalizes given $data. * Normalizes given $data.
* *
* @return null|scalar|array<mixed[]|scalar|null|object>|object * @param mixed $data
*
* @return mixed
*/ */
protected function normalize(mixed $data, int $depth = 0): mixed protected function normalize($data, int $depth = 0)
{ {
if ($depth > $this->maxNormalizeDepth) { if ($depth > $this->maxNormalizeDepth) {
return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization'; return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization';
@ -179,7 +192,7 @@ class JsonFormatter extends NormalizerFormatter
return $data; return $data;
} }
if ($data instanceof Stringable) { if (method_exists($data, '__toString')) {
return $data->__toString(); return $data->__toString();
} }
@ -197,7 +210,7 @@ class JsonFormatter extends NormalizerFormatter
* Normalizes given exception with or without its own stack trace based on * Normalizes given exception with or without its own stack trace based on
* `includeStacktraces` property. * `includeStacktraces` property.
* *
* @inheritDoc * {@inheritDoc}
*/ */
protected function normalizeException(Throwable $e, int $depth = 0): array protected function normalizeException(Throwable $e, int $depth = 0): array
{ {

View file

@ -11,9 +11,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Closure;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Formats incoming records into a one-line string * Formats incoming records into a one-line string
@ -27,16 +25,22 @@ class LineFormatter extends NormalizerFormatter
{ {
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
protected string $format; /** @var string */
protected bool $allowInlineLineBreaks; protected $format;
protected bool $ignoreEmptyContextAndExtra; /** @var bool */
protected bool $includeStacktraces; protected $allowInlineLineBreaks;
protected Closure|null $stacktracesParser = null; /** @var bool */
protected $ignoreEmptyContextAndExtra;
/** @var bool */
protected $includeStacktraces;
/** @var ?callable */
protected $stacktracesParser;
/** /**
* @param string|null $format The format of the message * @param string|null $format The format of the message
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
* @param bool $ignoreEmptyContextAndExtra
*/ */
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
{ {
@ -47,7 +51,7 @@ class LineFormatter extends NormalizerFormatter
parent::__construct($dateFormat); parent::__construct($dateFormat);
} }
public function includeStacktraces(bool $include = true, ?Closure $parser = null): self public function includeStacktraces(bool $include = true, ?callable $parser = null): self
{ {
$this->includeStacktraces = $include; $this->includeStacktraces = $include;
if ($this->includeStacktraces) { if ($this->includeStacktraces) {
@ -73,13 +77,14 @@ class LineFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record): string public function format(array $record): string
{ {
$vars = parent::format($record); $vars = parent::format($record);
$output = $this->format; $output = $this->format;
foreach ($vars['extra'] as $var => $val) { foreach ($vars['extra'] as $var => $val) {
if (false !== strpos($output, '%extra.'.$var.'%')) { if (false !== strpos($output, '%extra.'.$var.'%')) {
$output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
@ -95,12 +100,12 @@ class LineFormatter extends NormalizerFormatter
} }
if ($this->ignoreEmptyContextAndExtra) { if ($this->ignoreEmptyContextAndExtra) {
if (\count($vars['context']) === 0) { if (empty($vars['context'])) {
unset($vars['context']); unset($vars['context']);
$output = str_replace('%context%', '', $output); $output = str_replace('%context%', '', $output);
} }
if (\count($vars['extra']) === 0) { if (empty($vars['extra'])) {
unset($vars['extra']); unset($vars['extra']);
$output = str_replace('%extra%', '', $output); $output = str_replace('%extra%', '', $output);
} }
@ -117,7 +122,6 @@ class LineFormatter extends NormalizerFormatter
$output = preg_replace('/%(?:extra|context)\..+?%/', '', $output); $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
if (null === $output) { if (null === $output) {
$pcreErrorCode = preg_last_error(); $pcreErrorCode = preg_last_error();
throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
} }
} }
@ -147,7 +151,7 @@ class LineFormatter extends NormalizerFormatter
{ {
$str = $this->formatException($e); $str = $this->formatException($e);
if (($previous = $e->getPrevious()) instanceof \Throwable) { if ($previous = $e->getPrevious()) {
do { do {
$depth++; $depth++;
if ($depth > $this->maxNormalizeDepth) { if ($depth > $this->maxNormalizeDepth) {
@ -228,7 +232,7 @@ class LineFormatter extends NormalizerFormatter
{ {
$trace = $e->getTraceAsString(); $trace = $e->getTraceAsString();
if ($this->stacktracesParser !== null) { if ($this->stacktracesParser) {
$trace = $this->stacktracesParserCustom($trace); $trace = $this->stacktracesParserCustom($trace);
} }

View file

@ -11,8 +11,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\LogRecord;
/** /**
* Encodes message information into JSON in a format compatible with Loggly. * Encodes message information into JSON in a format compatible with Loggly.
* *
@ -35,13 +33,13 @@ class LogglyFormatter extends JsonFormatter
* @see https://www.loggly.com/docs/automated-parsing/#json * @see https://www.loggly.com/docs/automated-parsing/#json
* @see \Monolog\Formatter\JsonFormatter::format() * @see \Monolog\Formatter\JsonFormatter::format()
*/ */
protected function normalizeRecord(LogRecord $record): array public function format(array $record): string
{ {
$recordData = parent::normalizeRecord($record); if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTimeInterface)) {
$record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO");
unset($record["datetime"]);
}
$recordData["timestamp"] = $record->datetime->format("Y-m-d\TH:i:s.uO"); return parent::format($record);
unset($recordData["datetime"]);
return $recordData;
} }
} }

View file

@ -11,8 +11,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\LogRecord;
/** /**
* Encodes message information into JSON in a format compatible with Logmatic. * Encodes message information into JSON in a format compatible with Logmatic.
* *
@ -22,9 +20,15 @@ class LogmaticFormatter extends JsonFormatter
{ {
protected const MARKERS = ["sourcecode", "php"]; protected const MARKERS = ["sourcecode", "php"];
protected string $hostname = ''; /**
* @var string
*/
protected $hostname = '';
protected string $appName = ''; /**
* @var string
*/
protected $appname = '';
public function setHostname(string $hostname): self public function setHostname(string $hostname): self
{ {
@ -33,9 +37,9 @@ class LogmaticFormatter extends JsonFormatter
return $this; return $this;
} }
public function setAppName(string $appName): self public function setAppname(string $appname): self
{ {
$this->appName = $appName; $this->appname = $appname;
return $this; return $this;
} }
@ -46,19 +50,17 @@ class LogmaticFormatter extends JsonFormatter
* @see http://doc.logmatic.io/docs/basics-to-send-data * @see http://doc.logmatic.io/docs/basics-to-send-data
* @see \Monolog\Formatter\JsonFormatter::format() * @see \Monolog\Formatter\JsonFormatter::format()
*/ */
public function normalizeRecord(LogRecord $record): array public function format(array $record): string
{ {
$record = parent::normalizeRecord($record); if (!empty($this->hostname)) {
if ($this->hostname !== '') {
$record["hostname"] = $this->hostname; $record["hostname"] = $this->hostname;
} }
if ($this->appName !== '') { if (!empty($this->appname)) {
$record["appname"] = $this->appName; $record["appname"] = $this->appname;
} }
$record["@marker"] = static::MARKERS; $record["@marker"] = static::MARKERS;
return $record; return parent::format($record);
} }
} }

View file

@ -11,8 +11,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\LogRecord;
/** /**
* Serializes a log message to Logstash Event Format * Serializes a log message to Logstash Event Format
* *
@ -26,22 +24,22 @@ class LogstashFormatter extends NormalizerFormatter
/** /**
* @var string the name of the system for the Logstash log message, used to fill the @source field * @var string the name of the system for the Logstash log message, used to fill the @source field
*/ */
protected string $systemName; protected $systemName;
/** /**
* @var string an application name for the Logstash log message, used to fill the @type field * @var string an application name for the Logstash log message, used to fill the @type field
*/ */
protected string $applicationName; protected $applicationName;
/** /**
* @var string the key for 'extra' fields from the Monolog record * @var string the key for 'extra' fields from the Monolog record
*/ */
protected string $extraKey; protected $extraKey;
/** /**
* @var string the key for 'context' fields from the Monolog record * @var string the key for 'context' fields from the Monolog record
*/ */
protected string $contextKey; protected $contextKey;
/** /**
* @param string $applicationName The application that sends the data, used as the "type" field of logstash * @param string $applicationName The application that sends the data, used as the "type" field of logstash
@ -61,38 +59,41 @@ class LogstashFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function format(LogRecord $record): string public function format(array $record): string
{ {
$recordData = parent::format($record); $record = parent::format($record);
if (empty($record['datetime'])) {
$record['datetime'] = gmdate('c');
}
$message = [ $message = [
'@timestamp' => $recordData['datetime'], '@timestamp' => $record['datetime'],
'@version' => 1, '@version' => 1,
'host' => $this->systemName, 'host' => $this->systemName,
]; ];
if (isset($recordData['message'])) { if (isset($record['message'])) {
$message['message'] = $recordData['message']; $message['message'] = $record['message'];
} }
if (isset($recordData['channel'])) { if (isset($record['channel'])) {
$message['type'] = $recordData['channel']; $message['type'] = $record['channel'];
$message['channel'] = $recordData['channel']; $message['channel'] = $record['channel'];
} }
if (isset($recordData['level_name'])) { if (isset($record['level_name'])) {
$message['level'] = $recordData['level_name']; $message['level'] = $record['level_name'];
} }
if (isset($recordData['level'])) { if (isset($record['level'])) {
$message['monolog_level'] = $recordData['level']; $message['monolog_level'] = $record['level'];
} }
if ('' !== $this->applicationName) { if ($this->applicationName) {
$message['type'] = $this->applicationName; $message['type'] = $this->applicationName;
} }
if (\count($recordData['extra']) > 0) { if (!empty($record['extra'])) {
$message[$this->extraKey] = $recordData['extra']; $message[$this->extraKey] = $record['extra'];
} }
if (\count($recordData['context']) > 0) { if (!empty($record['context'])) {
$message[$this->contextKey] = $recordData['context']; $message[$this->contextKey] = $record['context'];
} }
return $this->toJson($message) . "\n"; return $this->toJson($message) . "\n";

View file

@ -14,7 +14,6 @@ namespace Monolog\Formatter;
use MongoDB\BSON\Type; use MongoDB\BSON\Type;
use MongoDB\BSON\UTCDateTime; use MongoDB\BSON\UTCDateTime;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Formats a record for use with the MongoDBHandler. * Formats a record for use with the MongoDBHandler.
@ -23,12 +22,15 @@ use Monolog\LogRecord;
*/ */
class MongoDBFormatter implements FormatterInterface class MongoDBFormatter implements FormatterInterface
{ {
private bool $exceptionTraceAsString; /** @var bool */
private int $maxNestingLevel; private $exceptionTraceAsString;
private bool $isLegacyMongoExt; /** @var int */
private $maxNestingLevel;
/** @var bool */
private $isLegacyMongoExt;
/** /**
* @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record->context is 2 * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
* @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
*/ */
public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true) public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true)
@ -40,20 +42,20 @@ class MongoDBFormatter implements FormatterInterface
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @return mixed[] * @return mixed[]
*/ */
public function format(LogRecord $record): array public function format(array $record): array
{ {
/** @var mixed[] $res */ /** @var mixed[] $res */
$res = $this->formatArray($record->toArray()); $res = $this->formatArray($record);
return $res; return $res;
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @return array<mixed[]> * @return array<mixed[]>
*/ */

View file

@ -14,7 +14,6 @@ namespace Monolog\Formatter;
use Monolog\DateTimeImmutable; use Monolog\DateTimeImmutable;
use Monolog\Utils; use Monolog\Utils;
use Throwable; use Throwable;
use Monolog\LogRecord;
/** /**
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
@ -25,11 +24,15 @@ class NormalizerFormatter implements FormatterInterface
{ {
public const SIMPLE_DATE = "Y-m-d\TH:i:sP"; public const SIMPLE_DATE = "Y-m-d\TH:i:sP";
protected string $dateFormat; /** @var string */
protected int $maxNormalizeDepth = 9; protected $dateFormat;
protected int $maxNormalizeItemCount = 1000; /** @var int */
protected $maxNormalizeDepth = 9;
/** @var int */
protected $maxNormalizeItemCount = 1000;
private int $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS; /** @var int */
private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
/** /**
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
@ -43,25 +46,17 @@ class NormalizerFormatter implements FormatterInterface
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/
public function format(LogRecord $record)
{
return $this->normalizeRecord($record);
}
/**
* Normalize an arbitrary value to a scalar|array|null
* *
* @return null|scalar|array<mixed[]|scalar|null> * @param mixed[] $record
*/ */
public function normalizeValue(mixed $data): mixed public function format(array $record)
{ {
return $this->normalize($data); return $this->normalize($record);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function formatBatch(array $records) public function formatBatch(array $records)
{ {
@ -129,25 +124,10 @@ class NormalizerFormatter implements FormatterInterface
} }
/** /**
* Provided as extension point * @param mixed $data
* * @return null|scalar|array<array|scalar|null>
* Because normalize is called with sub-values of context data etc, normalizeRecord can be
* extended when data needs to be appended on the record array but not to other normalized data.
*
* @return array<mixed[]|scalar|null>
*/ */
protected function normalizeRecord(LogRecord $record): array protected function normalize($data, int $depth = 0)
{
/** @var array<mixed> $normalized */
$normalized = $this->normalize($record->toArray());
return $normalized;
}
/**
* @return null|scalar|array<mixed[]|scalar|null>
*/
protected function normalize(mixed $data, int $depth = 0): mixed
{ {
if ($depth > $this->maxNormalizeDepth) { if ($depth > $this->maxNormalizeDepth) {
return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
@ -192,14 +172,14 @@ class NormalizerFormatter implements FormatterInterface
} }
if ($data instanceof \JsonSerializable) { if ($data instanceof \JsonSerializable) {
/** @var null|scalar|array<mixed[]|scalar|null> $value */ /** @var null|scalar|array<array|scalar|null> $value */
$value = $data->jsonSerialize(); $value = $data->jsonSerialize();
} elseif (method_exists($data, '__toString')) { } elseif (method_exists($data, '__toString')) {
/** @var string $value */ /** @var string $value */
$value = $data->__toString(); $value = $data->__toString();
} else { } else {
// the rest is normalized by json encoding and decoding it // the rest is normalized by json encoding and decoding it
/** @var null|scalar|array<mixed[]|scalar|null> $value */ /** @var null|scalar|array<array|scalar|null> $value */
$value = json_decode($this->toJson($data, true), true); $value = json_decode($this->toJson($data, true), true);
} }
@ -253,12 +233,12 @@ class NormalizerFormatter implements FormatterInterface
$trace = $e->getTrace(); $trace = $e->getTrace();
foreach ($trace as $frame) { foreach ($trace as $frame) {
if (isset($frame['file'], $frame['line'])) { if (isset($frame['file'])) {
$data['trace'][] = $frame['file'].':'.$frame['line']; $data['trace'][] = $frame['file'].':'.$frame['line'];
} }
} }
if (($previous = $e->getPrevious()) instanceof \Throwable) { if ($previous = $e->getPrevious()) {
$data['previous'] = $this->normalizeException($previous, $depth + 1); $data['previous'] = $this->normalizeException($previous, $depth + 1);
} }
@ -277,7 +257,10 @@ class NormalizerFormatter implements FormatterInterface
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors); return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
} }
protected function formatDate(\DateTimeInterface $date): string /**
* @return string
*/
protected function formatDate(\DateTimeInterface $date)
{ {
// in case the date format isn't custom then we defer to the custom DateTimeImmutable // in case the date format isn't custom then we defer to the custom DateTimeImmutable
// formatting logic, which will pick the right format based on whether useMicroseconds is on // formatting logic, which will pick the right format based on whether useMicroseconds is on

View file

@ -11,10 +11,8 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\LogRecord;
/** /**
* Formats data into an associative array of scalar (+ null) values. * Formats data into an associative array of scalar values.
* Objects and arrays will be JSON encoded. * Objects and arrays will be JSON encoded.
* *
* @author Andrew Lawson <adlawson@gmail.com> * @author Andrew Lawson <adlawson@gmail.com>
@ -22,21 +20,25 @@ use Monolog\LogRecord;
class ScalarFormatter extends NormalizerFormatter class ScalarFormatter extends NormalizerFormatter
{ {
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @phpstan-return array<string, scalar|null> $record * @phpstan-return array<string, scalar|null> $record
*/ */
public function format(LogRecord $record): array public function format(array $record): array
{ {
$result = []; $result = [];
foreach ($record->toArray() as $key => $value) { foreach ($record as $key => $value) {
$result[$key] = $this->toScalar($value); $result[$key] = $this->normalizeValue($value);
} }
return $result; return $result;
} }
protected function toScalar(mixed $value): string|int|float|bool|null /**
* @param mixed $value
* @return scalar|null
*/
protected function normalizeValue($value)
{ {
$normalized = $this->normalize($value); $normalized = $this->normalize($value);

View file

@ -1,66 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Monolog\Level;
use Monolog\LogRecord;
/**
* Serializes a log message according to RFC 5424
*
* @author Dalibor Karlović <dalibor.karlovic@sigwin.hr>
* @author Renat Gabdullin <renatobyj@gmail.com>
*/
class SyslogFormatter extends LineFormatter
{
private const SYSLOG_FACILITY_USER = 1;
private const FORMAT = "<%extra.priority%>1 %datetime% %extra.hostname% %extra.app-name% %extra.procid% %channel% %extra.structured-data% %level_name%: %message% %context% %extra%\n";
private const NILVALUE = '-';
private string $hostname;
private int $procid;
public function __construct(private string $applicationName = self::NILVALUE)
{
parent::__construct(self::FORMAT, 'Y-m-d\TH:i:s.uP', true, true);
$this->hostname = (string) gethostname();
$this->procid = (int) getmypid();
}
public function format(LogRecord $record): string
{
$record->extra = $this->formatExtra($record);
return parent::format($record);
}
/**
* @param LogRecord $record
* @return array<string, mixed>
*/
private function formatExtra(LogRecord $record): array
{
$extra = $record->extra;
$extra['app-name'] = $this->applicationName;
$extra['hostname'] = $this->hostname;
$extra['procid'] = $this->procid;
$extra['priority'] = self::calculatePriority($record->level);
$extra['structured-data'] = self::NILVALUE;
return $extra;
}
private static function calculatePriority(Level $level): int
{
return (self::SYSLOG_FACILITY_USER * 8) + $level->toRFC5424Level();
}
}

View file

@ -11,8 +11,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Serializes a log message according to Wildfire's header requirements * Serializes a log message according to Wildfire's header requirements
@ -20,9 +19,27 @@ use Monolog\LogRecord;
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com> * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
* @author Christophe Coevoet <stof@notk.org> * @author Christophe Coevoet <stof@notk.org>
* @author Kirill chEbba Chebunin <iam@chebba.org> * @author Kirill chEbba Chebunin <iam@chebba.org>
*
* @phpstan-import-type Level from \Monolog\Logger
*/ */
class WildfireFormatter extends NormalizerFormatter class WildfireFormatter extends NormalizerFormatter
{ {
/**
* Translates Monolog log levels to Wildfire levels.
*
* @var array<Level, string>
*/
private $logLevels = [
Logger::DEBUG => 'LOG',
Logger::INFO => 'INFO',
Logger::NOTICE => 'INFO',
Logger::WARNING => 'WARN',
Logger::ERROR => 'ERROR',
Logger::CRITICAL => 'ERROR',
Logger::ALERT => 'ERROR',
Logger::EMERGENCY => 'ERROR',
];
/** /**
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
*/ */
@ -35,61 +52,46 @@ class WildfireFormatter extends NormalizerFormatter
} }
/** /**
* Translates Monolog log levels to Wildfire levels. * {@inheritDoc}
* *
* @return 'LOG'|'INFO'|'WARN'|'ERROR' * @return string
*/ */
private function toWildfireLevel(Level $level): string public function format(array $record): string
{
return match ($level) {
Level::Debug => 'LOG',
Level::Info => 'INFO',
Level::Notice => 'INFO',
Level::Warning => 'WARN',
Level::Error => 'ERROR',
Level::Critical => 'ERROR',
Level::Alert => 'ERROR',
Level::Emergency => 'ERROR',
};
}
/**
* @inheritDoc
*/
public function format(LogRecord $record): string
{ {
// Retrieve the line and file if set and remove them from the formatted extra // Retrieve the line and file if set and remove them from the formatted extra
$file = $line = ''; $file = $line = '';
if (isset($record->extra['file'])) { if (isset($record['extra']['file'])) {
$file = $record->extra['file']; $file = $record['extra']['file'];
unset($record->extra['file']); unset($record['extra']['file']);
} }
if (isset($record->extra['line'])) { if (isset($record['extra']['line'])) {
$line = $record->extra['line']; $line = $record['extra']['line'];
unset($record->extra['line']); unset($record['extra']['line']);
} }
$message = ['message' => $record->message]; /** @var mixed[] $record */
$record = $this->normalize($record);
$message = ['message' => $record['message']];
$handleError = false; $handleError = false;
if (count($record->context) > 0) { if ($record['context']) {
$message['context'] = $this->normalize($record->context); $message['context'] = $record['context'];
$handleError = true; $handleError = true;
} }
if (count($record->extra) > 0) { if ($record['extra']) {
$message['extra'] = $this->normalize($record->extra); $message['extra'] = $record['extra'];
$handleError = true; $handleError = true;
} }
if (count($message) === 1) { if (count($message) === 1) {
$message = reset($message); $message = reset($message);
} }
if (is_array($message) && isset($message['context']['table'])) { if (isset($record['context']['table'])) {
$type = 'TABLE'; $type = 'TABLE';
$label = $record->channel .': '. $record->message; $label = $record['channel'] .': '. $record['message'];
$message = $message['context']['table']; $message = $record['context']['table'];
} else { } else {
$type = $this->toWildfireLevel($record->level); $type = $this->logLevels[$record['level']];
$label = $record->channel; $label = $record['channel'];
} }
// Create JSON object describing the appearance of the message in the console // Create JSON object describing the appearance of the message in the console
@ -112,7 +114,7 @@ class WildfireFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @phpstan-return never * @phpstan-return never
*/ */
@ -122,11 +124,11 @@ class WildfireFormatter extends NormalizerFormatter
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @return null|scalar|array<mixed[]|scalar|null>|object * @return null|scalar|array<array|scalar|null>|object
*/ */
protected function normalize(mixed $data, int $depth = 0): mixed protected function normalize($data, int $depth = 0)
{ {
if (is_object($data) && !$data instanceof \DateTimeInterface) { if (is_object($data) && !$data instanceof \DateTimeInterface) {
return $data; return $data;

View file

@ -11,50 +11,55 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Base Handler class providing basic level/bubble support * Base Handler class providing basic level/bubble support
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
abstract class AbstractHandler extends Handler implements ResettableInterface abstract class AbstractHandler extends Handler implements ResettableInterface
{ {
protected Level $level = Level::Debug; /**
protected bool $bubble = true; * @var int
* @phpstan-var Level
*/
protected $level = Logger::DEBUG;
/** @var bool */
protected $bubble = true;
/** /**
* @param int|string|Level|LogLevel::* $level The minimum logging level at which this handler will be triggered * @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level * @phpstan-param Level|LevelName|LogLevel::* $level
*/ */
public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($level = Logger::DEBUG, bool $bubble = true)
{ {
$this->setLevel($level); $this->setLevel($level);
$this->bubble = $bubble; $this->bubble = $bubble;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return $record->level->value >= $this->level->value; return $record['level'] >= $this->level;
} }
/** /**
* Sets minimum logging level at which this handler will be triggered. * Sets minimum logging level at which this handler will be triggered.
* *
* @param Level|LogLevel::* $level Level or level name * @param Level|LevelName|LogLevel::* $level Level or level name
* * @return self
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
*/ */
public function setLevel(int|string|Level $level): self public function setLevel($level): self
{ {
$this->level = Logger::toMonologLevel($level); $this->level = Logger::toMonologLevel($level);
@ -63,8 +68,12 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
/** /**
* Gets minimum logging level at which this handler will be triggered. * Gets minimum logging level at which this handler will be triggered.
*
* @return int
*
* @phpstan-return Level
*/ */
public function getLevel(): Level public function getLevel(): int
{ {
return $this->level; return $this->level;
} }
@ -74,6 +83,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
* *
* @param bool $bubble true means that this handler allows bubbling. * @param bool $bubble true means that this handler allows bubbling.
* false means that bubbling is not permitted. * false means that bubbling is not permitted.
* @return self
*/ */
public function setBubble(bool $bubble): self public function setBubble(bool $bubble): self
{ {
@ -94,9 +104,9 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function reset(): void public function reset()
{ {
} }
} }

View file

@ -11,8 +11,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\LogRecord;
/** /**
* Base Handler class providing the Handler structure, including processors and formatters * Base Handler class providing the Handler structure, including processors and formatters
* *
@ -20,6 +18,11 @@ use Monolog\LogRecord;
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org> * @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed}
*/ */
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{ {
@ -27,19 +30,20 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
use FormattableHandlerTrait; use FormattableHandlerTrait;
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (!$this->isHandling($record)) { if (!$this->isHandling($record)) {
return false; return false;
} }
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
$record->formatted = $this->getFormatter()->format($record); $record['formatted'] = $this->getFormatter()->format($record);
$this->write($record); $this->write($record);
@ -47,11 +51,16 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
} }
/** /**
* Writes the (already formatted) record down to the log of the implementing handler * Writes the record down to the log of the implementing handler
*
* @phpstan-param FormattedRecord $record
*/ */
abstract protected function write(LogRecord $record): void; abstract protected function write(array $record): void;
public function reset(): void /**
* @return void
*/
public function reset()
{ {
parent::reset(); parent::reset();

View file

@ -11,59 +11,70 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
/** /**
* Common syslog functionality * Common syslog functionality
*
* @phpstan-import-type Level from \Monolog\Logger
*/ */
abstract class AbstractSyslogHandler extends AbstractProcessingHandler abstract class AbstractSyslogHandler extends AbstractProcessingHandler
{ {
protected int $facility; /** @var int */
protected $facility;
/**
* Translates Monolog log levels to syslog log priorities.
* @var array
* @phpstan-var array<Level, int>
*/
protected $logLevels = [
Logger::DEBUG => LOG_DEBUG,
Logger::INFO => LOG_INFO,
Logger::NOTICE => LOG_NOTICE,
Logger::WARNING => LOG_WARNING,
Logger::ERROR => LOG_ERR,
Logger::CRITICAL => LOG_CRIT,
Logger::ALERT => LOG_ALERT,
Logger::EMERGENCY => LOG_EMERG,
];
/** /**
* List of valid log facility names. * List of valid log facility names.
* @var array<string, int> * @var array<string, int>
*/ */
protected array $facilities = [ protected $facilities = [
'auth' => \LOG_AUTH, 'auth' => LOG_AUTH,
'authpriv' => \LOG_AUTHPRIV, 'authpriv' => LOG_AUTHPRIV,
'cron' => \LOG_CRON, 'cron' => LOG_CRON,
'daemon' => \LOG_DAEMON, 'daemon' => LOG_DAEMON,
'kern' => \LOG_KERN, 'kern' => LOG_KERN,
'lpr' => \LOG_LPR, 'lpr' => LOG_LPR,
'mail' => \LOG_MAIL, 'mail' => LOG_MAIL,
'news' => \LOG_NEWS, 'news' => LOG_NEWS,
'syslog' => \LOG_SYSLOG, 'syslog' => LOG_SYSLOG,
'user' => \LOG_USER, 'user' => LOG_USER,
'uucp' => \LOG_UUCP, 'uucp' => LOG_UUCP,
]; ];
/**
* Translates Monolog log levels to syslog log priorities.
*/
protected function toSyslogPriority(Level $level): int
{
return $level->toRFC5424Level();
}
/** /**
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
*/ */
public function __construct(string|int $facility = \LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
if (!defined('PHP_WINDOWS_VERSION_BUILD')) { if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->facilities['local0'] = \LOG_LOCAL0; $this->facilities['local0'] = LOG_LOCAL0;
$this->facilities['local1'] = \LOG_LOCAL1; $this->facilities['local1'] = LOG_LOCAL1;
$this->facilities['local2'] = \LOG_LOCAL2; $this->facilities['local2'] = LOG_LOCAL2;
$this->facilities['local3'] = \LOG_LOCAL3; $this->facilities['local3'] = LOG_LOCAL3;
$this->facilities['local4'] = \LOG_LOCAL4; $this->facilities['local4'] = LOG_LOCAL4;
$this->facilities['local5'] = \LOG_LOCAL5; $this->facilities['local5'] = LOG_LOCAL5;
$this->facilities['local6'] = \LOG_LOCAL6; $this->facilities['local6'] = LOG_LOCAL6;
$this->facilities['local7'] = \LOG_LOCAL7; $this->facilities['local7'] = LOG_LOCAL7;
} else { } else {
$this->facilities['local0'] = 128; // LOG_LOCAL0 $this->facilities['local0'] = 128; // LOG_LOCAL0
$this->facilities['local1'] = 136; // LOG_LOCAL1 $this->facilities['local1'] = 136; // LOG_LOCAL1
@ -86,7 +97,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,38 +11,24 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\JsonFormatter; use Monolog\Formatter\JsonFormatter;
use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Channel\AMQPChannel;
use AMQPExchange; use AMQPExchange;
use Monolog\LogRecord;
/**
* @phpstan-import-type Record from \Monolog\Logger
*/
class AmqpHandler extends AbstractProcessingHandler class AmqpHandler extends AbstractProcessingHandler
{ {
protected AMQPExchange|AMQPChannel $exchange;
/** @var array<string, mixed> */
private array $extraAttributes = [];
protected string $exchangeName;
/** /**
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use * @var AMQPExchange|AMQPChannel $exchange
* @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only
*/ */
public function __construct(AMQPExchange|AMQPChannel $exchange, ?string $exchangeName = null, int|string|Level $level = Level::Debug, bool $bubble = true) protected $exchange;
{ /** @var array<string, mixed> */
if ($exchange instanceof AMQPChannel) { private $extraAttributes = [];
$this->exchangeName = (string) $exchangeName;
} elseif ($exchangeName !== null) {
@trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED);
}
$this->exchange = $exchange;
parent::__construct($level, $bubble);
}
/** /**
* @return array<string, mixed> * @return array<string, mixed>
@ -59,7 +45,7 @@ class AmqpHandler extends AbstractProcessingHandler
* message_id, user_id, app_id, delivery_mode, * message_id, user_id, app_id, delivery_mode,
* priority, timestamp, expiration, type * priority, timestamp, expiration, type
* or reply_to, headers. * or reply_to, headers.
* @return $this * @return AmqpHandler
*/ */
public function setExtraAttributes(array $extraAttributes): self public function setExtraAttributes(array $extraAttributes): self
{ {
@ -68,11 +54,34 @@ class AmqpHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * @var string
*/ */
protected function write(LogRecord $record): void protected $exchangeName;
/**
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
* @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only
*/
public function __construct($exchange, ?string $exchangeName = null, $level = Logger::DEBUG, bool $bubble = true)
{ {
$data = $record->formatted; if ($exchange instanceof AMQPChannel) {
$this->exchangeName = (string) $exchangeName;
} elseif (!$exchange instanceof AMQPExchange) {
throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required');
} elseif ($exchangeName) {
@trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED);
}
$this->exchange = $exchange;
parent::__construct($level, $bubble);
}
/**
* {@inheritDoc}
*/
protected function write(array $record): void
{
$data = $record["formatted"];
$routingKey = $this->getRoutingKey($record); $routingKey = $this->getRoutingKey($record);
if ($this->exchange instanceof AMQPExchange) { if ($this->exchange instanceof AMQPExchange) {
@ -80,7 +89,7 @@ class AmqpHandler extends AbstractProcessingHandler
'delivery_mode' => 2, 'delivery_mode' => 2,
'content_type' => 'application/json', 'content_type' => 'application/json',
]; ];
if (\count($this->extraAttributes) > 0) { if ($this->extraAttributes) {
$attributes = array_merge($attributes, $this->extraAttributes); $attributes = array_merge($attributes, $this->extraAttributes);
} }
$this->exchange->publish( $this->exchange->publish(
@ -99,7 +108,7 @@ class AmqpHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
@ -114,6 +123,7 @@ class AmqpHandler extends AbstractProcessingHandler
continue; continue;
} }
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
$data = $this->getFormatter()->format($record); $data = $this->getFormatter()->format($record);
@ -129,10 +139,12 @@ class AmqpHandler extends AbstractProcessingHandler
/** /**
* Gets the routing key for the AMQP exchange * Gets the routing key for the AMQP exchange
*
* @phpstan-param Record $record
*/ */
protected function getRoutingKey(LogRecord $record): string protected function getRoutingKey(array $record): string
{ {
$routingKey = sprintf('%s.%s', $record->level->name, $record->channel); $routingKey = sprintf('%s.%s', $record['level_name'], $record['channel']);
return strtolower($routingKey); return strtolower($routingKey);
} }
@ -149,7 +161,7 @@ class AmqpHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -14,30 +14,34 @@ namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
use function count; use function count;
use function headers_list; use function headers_list;
use function stripos; use function stripos;
use function trigger_error;
use const E_USER_DEPRECATED;
/** /**
* Handler sending logs to browser's javascript console with no browser extension required * Handler sending logs to browser's javascript console with no browser extension required
* *
* @author Olivier Poitrey <rs@dailymotion.com> * @author Olivier Poitrey <rs@dailymotion.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class BrowserConsoleHandler extends AbstractProcessingHandler class BrowserConsoleHandler extends AbstractProcessingHandler
{ {
protected static bool $initialized = false; /** @var bool */
protected static $initialized = false;
/** @var LogRecord[] */ /** @var FormattedRecord[] */
protected static array $records = []; protected static $records = [];
protected const FORMAT_HTML = 'html'; protected const FORMAT_HTML = 'html';
protected const FORMAT_JS = 'js'; protected const FORMAT_JS = 'js';
protected const FORMAT_UNKNOWN = 'unknown'; protected const FORMAT_UNKNOWN = 'unknown';
/** /**
* @inheritDoc * {@inheritDoc}
* *
* Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
* *
@ -51,9 +55,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
// Accumulate records // Accumulate records
static::$records[] = $record; static::$records[] = $record;
@ -76,11 +80,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return; return;
} }
if (count(static::$records) > 0) { if (count(static::$records)) {
if ($format === self::FORMAT_HTML) { if ($format === self::FORMAT_HTML) {
static::writeOutput('<script>' . self::generateScript() . '</script>'); static::writeOutput('<script>' . static::generateScript() . '</script>');
} else { // js format } elseif ($format === self::FORMAT_JS) {
static::writeOutput(self::generateScript()); static::writeOutput(static::generateScript());
} }
static::resetStatic(); static::resetStatic();
} }
@ -91,7 +95,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
self::resetStatic(); self::resetStatic();
} }
public function reset(): void public function reset()
{ {
parent::reset(); parent::reset();
@ -169,18 +173,18 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
{ {
$script = []; $script = [];
foreach (static::$records as $record) { foreach (static::$records as $record) {
$context = self::dump('Context', $record->context); $context = static::dump('Context', $record['context']);
$extra = self::dump('Extra', $record->extra); $extra = static::dump('Extra', $record['extra']);
if (\count($context) === 0 && \count($extra) === 0) { if (empty($context) && empty($extra)) {
$script[] = self::call_array('log', self::handleStyles($record->formatted)); $script[] = static::call_array('log', static::handleStyles($record['formatted']));
} else { } else {
$script = array_merge( $script = array_merge(
$script, $script,
[self::call_array('groupCollapsed', self::handleStyles($record->formatted))], [static::call_array('groupCollapsed', static::handleStyles($record['formatted']))],
$context, $context,
$extra, $extra,
[self::call('groupEnd')] [static::call('groupEnd')]
); );
} }
} }
@ -199,14 +203,14 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
foreach (array_reverse($matches) as $match) { foreach (array_reverse($matches) as $match) {
$args[] = '"font-weight: normal"'; $args[] = '"font-weight: normal"';
$args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
$pos = $match[0][1]; $pos = $match[0][1];
$format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0])); $format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0]));
} }
$args[] = self::quote('font-weight: normal'); $args[] = static::quote('font-weight: normal');
$args[] = self::quote($format); $args[] = static::quote($format);
return array_reverse($args); return array_reverse($args);
} }
@ -232,7 +236,6 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
if (null === $style) { if (null === $style) {
$pcreErrorCode = preg_last_error(); $pcreErrorCode = preg_last_error();
throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
} }
@ -247,16 +250,16 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
{ {
$script = []; $script = [];
$dict = array_filter($dict); $dict = array_filter($dict);
if (\count($dict) === 0) { if (empty($dict)) {
return $script; return $script;
} }
$script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title));
foreach ($dict as $key => $value) { foreach ($dict as $key => $value) {
$value = json_encode($value); $value = json_encode($value);
if (empty($value)) { if (empty($value)) {
$value = self::quote(''); $value = static::quote('');
} }
$script[] = self::call('log', self::quote('%s: %o'), self::quote((string) $key), $value); $script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value);
} }
return $script; return $script;
@ -277,7 +280,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true)); throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
} }
return self::call_array($method, $args); return static::call_array($method, $args);
} }
/** /**

View file

@ -11,10 +11,9 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Buffers all records until closing the handler and then pass them as batch. * Buffers all records until closing the handler and then pass them as batch.
@ -23,30 +22,32 @@ use Monolog\LogRecord;
* sending one per log message. * sending one per log message.
* *
* @author Christophe Coevoet <stof@notk.org> * @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{ {
use ProcessableHandlerTrait; use ProcessableHandlerTrait;
protected HandlerInterface $handler; /** @var HandlerInterface */
protected $handler;
protected int $bufferSize = 0; /** @var int */
protected $bufferSize = 0;
protected int $bufferLimit; /** @var int */
protected $bufferLimit;
protected bool $flushOnOverflow; /** @var bool */
protected $flushOnOverflow;
/** @var LogRecord[] */ /** @var Record[] */
protected array $buffer = []; protected $buffer = [];
/** @var bool */
protected bool $initialized = false; protected $initialized = false;
/** /**
* @param HandlerInterface $handler Handler. * @param HandlerInterface $handler Handler.
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
*/ */
public function __construct(HandlerInterface $handler, int $bufferLimit = 0, int|string|Level $level = Level::Debug, bool $bubble = true, bool $flushOnOverflow = false) public function __construct(HandlerInterface $handler, int $bufferLimit = 0, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->handler = $handler; $this->handler = $handler;
@ -55,11 +56,11 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if ($record->level->isLowerThan($this->level)) { if ($record['level'] < $this->level) {
return false; return false;
} }
@ -78,7 +79,8 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
} }
} }
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
@ -106,7 +108,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
@ -124,7 +126,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
$this->buffer = []; $this->buffer = [];
} }
public function reset(): void public function reset()
{ {
$this->flush(); $this->flush();
@ -138,7 +140,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -152,7 +154,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {

View file

@ -13,10 +13,8 @@ namespace Monolog\Handler;
use Monolog\Formatter\ChromePHPFormatter; use Monolog\Formatter\ChromePHPFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
use Monolog\DateTimeImmutable;
/** /**
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
@ -24,6 +22,8 @@ use Monolog\DateTimeImmutable;
* This also works out of the box with Firefox 43+ * This also works out of the box with Firefox 43+
* *
* @author Christophe Coevoet <stof@notk.org> * @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class ChromePHPHandler extends AbstractProcessingHandler class ChromePHPHandler extends AbstractProcessingHandler
{ {
@ -44,25 +44,29 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/ */
protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
protected static bool $initialized = false; /** @var bool */
protected static $initialized = false;
/** /**
* Tracks whether we sent too much data * Tracks whether we sent too much data
* *
* Chrome limits the headers to 4KB, so when we sent 3KB we stop sending * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending
*
* @var bool
*/ */
protected static bool $overflowed = false; protected static $overflowed = false;
/** @var mixed[] */ /** @var mixed[] */
protected static array $json = [ protected static $json = [
'version' => self::VERSION, 'version' => self::VERSION,
'columns' => ['label', 'log', 'backtrace', 'type'], 'columns' => ['label', 'log', 'backtrace', 'type'],
'rows' => [], 'rows' => [],
]; ];
protected static bool $sendHeaders = true; /** @var bool */
protected static $sendHeaders = true;
public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
if (!function_exists('json_encode')) { if (!function_exists('json_encode')) {
@ -71,7 +75,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
@ -82,15 +86,15 @@ class ChromePHPHandler extends AbstractProcessingHandler
$messages = []; $messages = [];
foreach ($records as $record) { foreach ($records as $record) {
if ($record->level < $this->level) { if ($record['level'] < $this->level) {
continue; continue;
} }
/** @var Record $message */
$message = $this->processRecord($record); $message = $this->processRecord($record);
$messages[] = $message; $messages[] = $message;
} }
if (\count($messages) > 0) { if (!empty($messages)) {
$messages = $this->getFormatter()->formatBatch($messages); $messages = $this->getFormatter()->formatBatch($messages);
self::$json['rows'] = array_merge(self::$json['rows'], $messages); self::$json['rows'] = array_merge(self::$json['rows'], $messages);
$this->send(); $this->send();
@ -98,7 +102,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {
@ -111,13 +115,13 @@ class ChromePHPHandler extends AbstractProcessingHandler
* @see sendHeader() * @see sendHeader()
* @see send() * @see send()
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!$this->isWebRequest()) { if (!$this->isWebRequest()) {
return; return;
} }
self::$json['rows'][] = $record->formatted; self::$json['rows'][] = $record['formatted'];
$this->send(); $this->send();
} }
@ -149,12 +153,15 @@ class ChromePHPHandler extends AbstractProcessingHandler
if (strlen($data) > 3 * 1024) { if (strlen($data) > 3 * 1024) {
self::$overflowed = true; self::$overflowed = true;
$record = new LogRecord( $record = [
message: 'Incomplete logs, chrome header size limit reached', 'message' => 'Incomplete logs, chrome header size limit reached',
level: Level::Warning, 'context' => [],
channel: 'monolog', 'level' => Logger::WARNING,
datetime: new DateTimeImmutable(true), 'level_name' => Logger::getLevelName(Logger::WARNING),
); 'channel' => 'monolog',
'datetime' => new \DateTimeImmutable(),
'extra' => [],
];
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
$json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true);
$data = base64_encode($json); $data = base64_encode($json);
@ -180,7 +187,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/ */
protected function headersAccepted(): bool protected function headersAccepted(): bool
{ {
if (!isset($_SERVER['HTTP_USER_AGENT'])) { if (empty($_SERVER['HTTP_USER_AGENT'])) {
return false; return false;
} }

View file

@ -13,42 +13,22 @@ namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\JsonFormatter; use Monolog\Formatter\JsonFormatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* CouchDB handler * CouchDB handler
* *
* @author Markus Bachmann <markus.bachmann@bachi.biz> * @author Markus Bachmann <markus.bachmann@bachi.biz>
* @phpstan-type Options array{
* host: string,
* port: int,
* dbname: string,
* username: string|null,
* password: string|null
* }
* @phpstan-type InputOptions array{
* host?: string,
* port?: int,
* dbname?: string,
* username?: string|null,
* password?: string|null
* }
*/ */
class CouchDBHandler extends AbstractProcessingHandler class CouchDBHandler extends AbstractProcessingHandler
{ {
/** /** @var mixed[] */
* @var mixed[] private $options;
* @phpstan-var Options
*/
private array $options;
/** /**
* @param mixed[] $options * @param mixed[] $options
*
* @phpstan-param InputOptions $options
*/ */
public function __construct(array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{ {
$this->options = array_merge([ $this->options = array_merge([
'host' => 'localhost', 'host' => 'localhost',
@ -62,12 +42,12 @@ class CouchDBHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$basicAuth = null; $basicAuth = null;
if (null !== $this->options['username'] && null !== $this->options['password']) { if ($this->options['username']) {
$basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
} }
@ -75,7 +55,7 @@ class CouchDBHandler extends AbstractProcessingHandler
$context = stream_context_create([ $context = stream_context_create([
'http' => [ 'http' => [
'method' => 'POST', 'method' => 'POST',
'content' => $record->formatted, 'content' => $record['formatted'],
'ignore_errors' => true, 'ignore_errors' => true,
'max_redirects' => 0, 'max_redirects' => 0,
'header' => 'Content-type: application/json', 'header' => 'Content-type: application/json',
@ -88,7 +68,7 @@ class CouchDBHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,9 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Logs to Cube. * Logs to Cube.
@ -24,13 +23,18 @@ use Monolog\LogRecord;
*/ */
class CubeHandler extends AbstractProcessingHandler class CubeHandler extends AbstractProcessingHandler
{ {
private ?\Socket $udpConnection = null; /** @var resource|\Socket|null */
private ?\CurlHandle $httpConnection = null; private $udpConnection = null;
private string $scheme; /** @var resource|\CurlHandle|null */
private string $host; private $httpConnection = null;
private int $port; /** @var string */
private $scheme;
/** @var string */
private $host;
/** @var int */
private $port;
/** @var string[] */ /** @var string[] */
private array $acceptedSchemes = ['http', 'udp']; private $acceptedSchemes = ['http', 'udp'];
/** /**
* Create a Cube handler * Create a Cube handler
@ -39,7 +43,7 @@ class CubeHandler extends AbstractProcessingHandler
* A valid url must consist of three parts : protocol://host:port * A valid url must consist of three parts : protocol://host:port
* Only valid protocols used by Cube are http and udp * Only valid protocols used by Cube are http and udp
*/ */
public function __construct(string $url, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(string $url, $level = Logger::DEBUG, bool $bubble = true)
{ {
$urlInfo = parse_url($url); $urlInfo = parse_url($url);
@ -47,7 +51,7 @@ class CubeHandler extends AbstractProcessingHandler
throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
} }
if (!in_array($urlInfo['scheme'], $this->acceptedSchemes, true)) { if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) {
throw new \UnexpectedValueException( throw new \UnexpectedValueException(
'Invalid protocol (' . $urlInfo['scheme'] . ').' 'Invalid protocol (' . $urlInfo['scheme'] . ').'
. ' Valid options are ' . implode(', ', $this->acceptedSchemes) . ' Valid options are ' . implode(', ', $this->acceptedSchemes)
@ -56,7 +60,7 @@ class CubeHandler extends AbstractProcessingHandler
$this->scheme = $urlInfo['scheme']; $this->scheme = $urlInfo['scheme'];
$this->host = $urlInfo['host']; $this->host = $urlInfo['host'];
$this->port = $urlInfo['port']; $this->port = (int) $urlInfo['port'];
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
} }
@ -107,24 +111,24 @@ class CubeHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$date = $record->datetime; $date = $record['datetime'];
$data = ['time' => $date->format('Y-m-d\TH:i:s.uO')]; $data = ['time' => $date->format('Y-m-d\TH:i:s.uO')];
$context = $record->context; unset($record['datetime']);
if (isset($context['type'])) { if (isset($record['context']['type'])) {
$data['type'] = $context['type']; $data['type'] = $record['context']['type'];
unset($context['type']); unset($record['context']['type']);
} else { } else {
$data['type'] = $record->channel; $data['type'] = $record['channel'];
} }
$data['data'] = $context; $data['data'] = $record['context'];
$data['data']['level'] = $record->level; $data['data']['level'] = $record['level'];
if ($this->scheme === 'http') { if ($this->scheme === 'http') {
$this->writeHttp(Utils::jsonEncode($data)); $this->writeHttp(Utils::jsonEncode($data));
@ -135,20 +139,16 @@ class CubeHandler extends AbstractProcessingHandler
private function writeUdp(string $data): void private function writeUdp(string $data): void
{ {
if (null === $this->udpConnection) { if (!$this->udpConnection) {
$this->connectUdp(); $this->connectUdp();
} }
if (null === $this->udpConnection) {
throw new \LogicException('No UDP socket could be opened');
}
socket_send($this->udpConnection, $data, strlen($data), 0); socket_send($this->udpConnection, $data, strlen($data), 0);
} }
private function writeHttp(string $data): void private function writeHttp(string $data): void
{ {
if (null === $this->httpConnection) { if (!$this->httpConnection) {
$this->connectHttp(); $this->connectHttp();
} }

View file

@ -21,7 +21,7 @@ use CurlHandle;
final class Util final class Util
{ {
/** @var array<int> */ /** @var array<int> */
private static array $retriableErrorCodes = [ private static $retriableErrorCodes = [
CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_CONNECT, CURLE_COULDNT_CONNECT,
CURLE_HTTP_NOT_FOUND, CURLE_HTTP_NOT_FOUND,
@ -34,17 +34,19 @@ final class Util
/** /**
* Executes a CURL request with optional retries and exception on failure * Executes a CURL request with optional retries and exception on failure
* *
* @param CurlHandle $ch curl handler * @param resource|CurlHandle $ch curl handler
* @param int $retries
* @param bool $closeAfterDone
* @return bool|string @see curl_exec * @return bool|string @see curl_exec
*/ */
public static function execute(CurlHandle $ch, int $retries = 5, bool $closeAfterDone = true) public static function execute($ch, int $retries = 5, bool $closeAfterDone = true)
{ {
while ($retries--) { while ($retries--) {
$curlResponse = curl_exec($ch); $curlResponse = curl_exec($ch);
if ($curlResponse === false) { if ($curlResponse === false) {
$curlErrno = curl_errno($ch); $curlErrno = curl_errno($ch);
if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || $retries === 0) { if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) {
$curlError = curl_error($ch); $curlError = curl_error($ch);
if ($closeAfterDone) { if ($closeAfterDone) {

View file

@ -11,10 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Simple handler wrapper that deduplicates log records across multiple requests * Simple handler wrapper that deduplicates log records across multiple requests
@ -35,29 +33,45 @@ use Monolog\LogRecord;
* same way. * same way.
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/ */
class DeduplicationHandler extends BufferHandler class DeduplicationHandler extends BufferHandler
{ {
protected string $deduplicationStore; /**
* @var string
*/
protected $deduplicationStore;
protected Level $deduplicationLevel; /**
* @var Level
*/
protected $deduplicationLevel;
protected int $time; /**
* @var int
*/
protected $time;
private bool $gc = false; /**
* @var bool
*/
private $gc = false;
/** /**
* @param HandlerInterface $handler Handler. * @param HandlerInterface $handler Handler.
* @param string $deduplicationStore The file/path where the deduplication log should be kept * @param string $deduplicationStore The file/path where the deduplication log should be kept
* @param int|string|Level|LogLevel::* $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes * @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $deduplicationLevel * @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel
*/ */
public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, int|string|Level $deduplicationLevel = Level::Error, int $time = 60, bool $bubble = true) public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true)
{ {
parent::__construct($handler, 0, Level::Debug, $bubble, false); parent::__construct($handler, 0, Logger::DEBUG, $bubble, false);
$this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore;
$this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel);
@ -73,8 +87,8 @@ class DeduplicationHandler extends BufferHandler
$passthru = null; $passthru = null;
foreach ($this->buffer as $record) { foreach ($this->buffer as $record) {
if ($record->level->value >= $this->deduplicationLevel->value) { if ($record['level'] >= $this->deduplicationLevel) {
$passthru = $passthru === true || !$this->isDuplicate($record); $passthru = $passthru || !$this->isDuplicate($record);
if ($passthru) { if ($passthru) {
$this->appendRecord($record); $this->appendRecord($record);
} }
@ -93,7 +107,10 @@ class DeduplicationHandler extends BufferHandler
} }
} }
private function isDuplicate(LogRecord $record): bool /**
* @phpstan-param Record $record
*/
private function isDuplicate(array $record): bool
{ {
if (!file_exists($this->deduplicationStore)) { if (!file_exists($this->deduplicationStore)) {
return false; return false;
@ -105,13 +122,13 @@ class DeduplicationHandler extends BufferHandler
} }
$yesterday = time() - 86400; $yesterday = time() - 86400;
$timestampValidity = $record->datetime->getTimestamp() - $this->time; $timestampValidity = $record['datetime']->getTimestamp() - $this->time;
$expectedMessage = preg_replace('{[\r\n].*}', '', $record->message); $expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']);
for ($i = count($store) - 1; $i >= 0; $i--) { for ($i = count($store) - 1; $i >= 0; $i--) {
list($timestamp, $level, $message) = explode(':', $store[$i], 3); list($timestamp, $level, $message) = explode(':', $store[$i], 3);
if ($level === $record->level->getName() && $message === $expectedMessage && $timestamp > $timestampValidity) { if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) {
return true; return true;
} }
@ -131,7 +148,7 @@ class DeduplicationHandler extends BufferHandler
$handle = fopen($this->deduplicationStore, 'rw+'); $handle = fopen($this->deduplicationStore, 'rw+');
if (false === $handle) { if (!$handle) {
throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore); throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore);
} }
@ -142,7 +159,7 @@ class DeduplicationHandler extends BufferHandler
while (!feof($handle)) { while (!feof($handle)) {
$log = fgets($handle); $log = fgets($handle);
if (is_string($log) && '' !== $log && substr($log, 0, 10) >= $timestampValidity) { if ($log && substr($log, 0, 10) >= $timestampValidity) {
$validLogs[] = $log; $validLogs[] = $log;
} }
} }
@ -159,8 +176,11 @@ class DeduplicationHandler extends BufferHandler
$this->gc = false; $this->gc = false;
} }
private function appendRecord(LogRecord $record): void /**
* @phpstan-param Record $record
*/
private function appendRecord(array $record): void
{ {
file_put_contents($this->deduplicationStore, $record->datetime->getTimestamp() . ':' . $record->level->getName() . ':' . preg_replace('{[\r\n].*}', '', $record->message) . "\n", FILE_APPEND); file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND);
} }
} }

View file

@ -11,11 +11,10 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\NormalizerFormatter; use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Doctrine\CouchDB\CouchDBClient; use Doctrine\CouchDB\CouchDBClient;
use Monolog\LogRecord;
/** /**
* CouchDB handler for Doctrine CouchDB ODM * CouchDB handler for Doctrine CouchDB ODM
@ -24,20 +23,21 @@ use Monolog\LogRecord;
*/ */
class DoctrineCouchDBHandler extends AbstractProcessingHandler class DoctrineCouchDBHandler extends AbstractProcessingHandler
{ {
private CouchDBClient $client; /** @var CouchDBClient */
private $client;
public function __construct(CouchDBClient $client, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true)
{ {
$this->client = $client; $this->client = $client;
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->client->postDocument($record->formatted); $this->client->postDocument($record['formatted']);
} }
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface

View file

@ -16,8 +16,7 @@ use Aws\DynamoDb\DynamoDbClient;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Aws\DynamoDb\Marshaler; use Aws\DynamoDb\Marshaler;
use Monolog\Formatter\ScalarFormatter; use Monolog\Formatter\ScalarFormatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/)
@ -29,15 +28,35 @@ class DynamoDbHandler extends AbstractProcessingHandler
{ {
public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO';
protected DynamoDbClient $client; /**
* @var DynamoDbClient
*/
protected $client;
protected string $table; /**
* @var string
*/
protected $table;
protected Marshaler $marshaler; /**
* @var int
*/
protected $version;
public function __construct(DynamoDbClient $client, string $table, int|string|Level $level = Level::Debug, bool $bubble = true) /**
* @var Marshaler
*/
protected $marshaler;
public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true)
{ {
/** @phpstan-ignore-next-line */
if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) {
$this->version = 3;
$this->marshaler = new Marshaler; $this->marshaler = new Marshaler;
} else {
$this->version = 2;
}
$this->client = $client; $this->client = $client;
$this->table = $table; $this->table = $table;
@ -46,12 +65,17 @@ class DynamoDbHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$filtered = $this->filterEmptyFields($record->formatted); $filtered = $this->filterEmptyFields($record['formatted']);
if ($this->version === 3) {
$formatted = $this->marshaler->marshalItem($filtered); $formatted = $this->marshaler->marshalItem($filtered);
} else {
/** @phpstan-ignore-next-line */
$formatted = $this->client->formatAttributes($filtered);
}
$this->client->putItem([ $this->client->putItem([
'TableName' => $this->table, 'TableName' => $this->table,
@ -66,12 +90,12 @@ class DynamoDbHandler extends AbstractProcessingHandler
protected function filterEmptyFields(array $record): array protected function filterEmptyFields(array $record): array
{ {
return array_filter($record, function ($value) { return array_filter($record, function ($value) {
return [] !== $value; return !empty($value) || false === $value || 0 === $value;
}); });
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -14,10 +14,9 @@ namespace Monolog\Handler;
use Elastica\Document; use Elastica\Document;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticaFormatter; use Monolog\Formatter\ElasticaFormatter;
use Monolog\Level; use Monolog\Logger;
use Elastica\Client; use Elastica\Client;
use Elastica\Exception\ExceptionInterface; use Elastica\Exception\ExceptionInterface;
use Monolog\LogRecord;
/** /**
* Elastic Search handler * Elastic Search handler
@ -34,34 +33,24 @@ use Monolog\LogRecord;
* $log->pushHandler($handler); * $log->pushHandler($handler);
* *
* @author Jelle Vink <jelle.vink@gmail.com> * @author Jelle Vink <jelle.vink@gmail.com>
* @phpstan-type Options array{
* index: string,
* type: string,
* ignore_error: bool
* }
* @phpstan-type InputOptions array{
* index?: string,
* type?: string,
* ignore_error?: bool
* }
*/ */
class ElasticaHandler extends AbstractProcessingHandler class ElasticaHandler extends AbstractProcessingHandler
{ {
protected Client $client; /**
* @var Client
*/
protected $client;
/** /**
* @var mixed[] Handler config options * @var mixed[] Handler config options
* @phpstan-var Options
*/ */
protected array $options; protected $options = [];
/** /**
* @param Client $client Elastica Client object * @param Client $client Elastica Client object
* @param mixed[] $options Handler configuration * @param mixed[] $options Handler configuration
*
* @phpstan-param InputOptions $options
*/ */
public function __construct(Client $client, array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->client = $client; $this->client = $client;
@ -76,15 +65,15 @@ class ElasticaHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->bulkSend([$record->formatted]); $this->bulkSend([$record['formatted']]);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -97,8 +86,6 @@ class ElasticaHandler extends AbstractProcessingHandler
/** /**
* @return mixed[] * @return mixed[]
*
* @phpstan-return Options
*/ */
public function getOptions(): array public function getOptions(): array
{ {
@ -106,7 +93,7 @@ class ElasticaHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {
@ -114,7 +101,7 @@ class ElasticaHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {

View file

@ -14,13 +14,12 @@ namespace Monolog\Handler;
use Elastic\Elasticsearch\Response\Elasticsearch; use Elastic\Elasticsearch\Response\Elasticsearch;
use Throwable; use Throwable;
use RuntimeException; use RuntimeException;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticsearchFormatter; use Monolog\Formatter\ElasticsearchFormatter;
use InvalidArgumentException; use InvalidArgumentException;
use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException; use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException;
use Elasticsearch\Client; use Elasticsearch\Client;
use Monolog\LogRecord;
use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException; use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException;
use Elastic\Elasticsearch\Client as Client8; use Elastic\Elasticsearch\Client as Client8;
@ -44,26 +43,18 @@ use Elastic\Elasticsearch\Client as Client8;
* $log->pushHandler($handler); * $log->pushHandler($handler);
* *
* @author Avtandil Kikabidze <akalongman@gmail.com> * @author Avtandil Kikabidze <akalongman@gmail.com>
* @phpstan-type Options array{
* index: string,
* type: string,
* ignore_error: bool
* }
* @phpstan-type InputOptions array{
* index?: string,
* type?: string,
* ignore_error?: bool
* }
*/ */
class ElasticsearchHandler extends AbstractProcessingHandler class ElasticsearchHandler extends AbstractProcessingHandler
{ {
protected Client|Client8 $client; /**
* @var Client|Client8
*/
protected $client;
/** /**
* @var mixed[] Handler config options * @var mixed[] Handler config options
* @phpstan-var Options
*/ */
protected array $options; protected $options = [];
/** /**
* @var bool * @var bool
@ -73,11 +64,13 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/** /**
* @param Client|Client8 $client Elasticsearch Client object * @param Client|Client8 $client Elasticsearch Client object
* @param mixed[] $options Handler configuration * @param mixed[] $options Handler configuration
*
* @phpstan-param InputOptions $options
*/ */
public function __construct(Client|Client8 $client, array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{ {
if (!$client instanceof Client && !$client instanceof Client8) {
throw new \TypeError('Elasticsearch\Client or Elastic\Elasticsearch\Client instance required');
}
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->client = $client; $this->client = $client;
$this->options = array_merge( $this->options = array_merge(
@ -99,15 +92,15 @@ class ElasticsearchHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->bulkSend([$record->formatted]); $this->bulkSend([$record['formatted']]);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -122,8 +115,6 @@ class ElasticsearchHandler extends AbstractProcessingHandler
* Getter options * Getter options
* *
* @return mixed[] * @return mixed[]
*
* @phpstan-return Options
*/ */
public function getOptions(): array public function getOptions(): array
{ {
@ -131,7 +122,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {
@ -139,7 +130,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
@ -150,7 +141,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/** /**
* Use Elasticsearch bulk API to send list of documents * Use Elasticsearch bulk API to send list of documents
* *
* @param array<array<mixed>> $records Records + _index/_type keys * @param array[] $records Records + _index/_type keys
* @throws \RuntimeException * @throws \RuntimeException
*/ */
protected function bulkSend(array $records): void protected function bulkSend(array $records): void

View file

@ -13,9 +13,8 @@ namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Stores to PHP error_log() handler. * Stores to PHP error_log() handler.
@ -27,14 +26,16 @@ class ErrorLogHandler extends AbstractProcessingHandler
public const OPERATING_SYSTEM = 0; public const OPERATING_SYSTEM = 0;
public const SAPI = 4; public const SAPI = 4;
protected int $messageType; /** @var int */
protected bool $expandNewlines; protected $messageType;
/** @var bool */
protected $expandNewlines;
/** /**
* @param int $messageType Says where the error should go. * @param int $messageType Says where the error should go.
* @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
*/ */
public function __construct(int $messageType = self::OPERATING_SYSTEM, int|string|Level $level = Level::Debug, bool $bubble = true, bool $expandNewlines = false) public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -60,7 +61,7 @@ class ErrorLogHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {
@ -68,20 +69,19 @@ class ErrorLogHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!$this->expandNewlines) { if (!$this->expandNewlines) {
error_log((string) $record->formatted, $this->messageType); error_log((string) $record['formatted'], $this->messageType);
return; return;
} }
$lines = preg_split('{[\r\n]+}', (string) $record->formatted); $lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
if ($lines === false) { if ($lines === false) {
$pcreErrorCode = preg_last_error(); $pcreErrorCode = preg_last_error();
throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode)); throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode));
} }
foreach ($lines as $line) { foreach ($lines as $line) {

View file

@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Throwable; use Throwable;
use Monolog\LogRecord;
/** /**
* Forwards records to at most one handler * Forwards records to at most one handler
@ -20,15 +19,18 @@ use Monolog\LogRecord;
* If a handler fails, the exception is suppressed and the record is forwarded to the next handler. * If a handler fails, the exception is suppressed and the record is forwarded to the next handler.
* *
* As soon as one handler handles a record successfully, the handling stops there. * As soon as one handler handles a record successfully, the handling stops there.
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class FallbackGroupHandler extends GroupHandler class FallbackGroupHandler extends GroupHandler
{ {
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
foreach ($this->handlers as $handler) { foreach ($this->handlers as $handler) {
@ -44,15 +46,16 @@ class FallbackGroupHandler extends GroupHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
$processed = []; $processed = [];
foreach ($records as $record) { foreach ($records as $record) {
$processed[] = $this->processRecord($record); $processed[] = $this->processRecord($record);
} }
/** @var Record[] $records */
$records = $processed; $records = $processed;
} }

View file

@ -11,13 +11,10 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Closure;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Simple handler wrapper that filters records based on a list of levels * Simple handler wrapper that filters records based on a list of levels
@ -26,99 +23,110 @@ use Monolog\LogRecord;
* *
* @author Hennadiy Verkh * @author Hennadiy Verkh
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{ {
use ProcessableHandlerTrait; use ProcessableHandlerTrait;
/** /**
* Handler or factory Closure($record, $this) * Handler or factory callable($record, $this)
* *
* @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface * @var callable|HandlerInterface
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
*/ */
protected Closure|HandlerInterface $handler; protected $handler;
/** /**
* Minimum level for logs that are passed to handler * Minimum level for logs that are passed to handler
* *
* @var bool[] Map of Level value => true * @var int[]
* @phpstan-var array<value-of<Level::VALUES>, true> * @phpstan-var array<Level, int>
*/ */
protected array $acceptedLevels; protected $acceptedLevels;
/** /**
* Whether the messages that are handled can bubble up the stack or not * Whether the messages that are handled can bubble up the stack or not
*
* @var bool
*/ */
protected bool $bubble; protected $bubble;
/** /**
* @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
* *
* @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $filterHandler). * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler).
* @param int|string|Level|array<int|string|Level|LogLevel::*> $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
* @param int|string|Level|LogLevel::* $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array * @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|array<value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $minLevelOrList * @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $maxLevel * @phpstan-param Level|LevelName|LogLevel::* $maxLevel
*/ */
public function __construct(Closure|HandlerInterface $handler, int|string|Level|array $minLevelOrList = Level::Debug, int|string|Level $maxLevel = Level::Emergency, bool $bubble = true) public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true)
{ {
$this->handler = $handler; $this->handler = $handler;
$this->bubble = $bubble; $this->bubble = $bubble;
$this->setAcceptedLevels($minLevelOrList, $maxLevel); $this->setAcceptedLevels($minLevelOrList, $maxLevel);
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
}
} }
/** /**
* @phpstan-return list<Level> List of levels * @phpstan-return array<int, Level>
*/ */
public function getAcceptedLevels(): array public function getAcceptedLevels(): array
{ {
return array_map(fn (int $level) => Level::from($level), array_keys($this->acceptedLevels)); return array_flip($this->acceptedLevels);
} }
/** /**
* @param int|string|Level|LogLevel::*|array<int|string|Level|LogLevel::*> $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
* @param int|string|Level|LogLevel::* $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|array<value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $minLevelOrList * @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $maxLevel * @phpstan-param Level|LevelName|LogLevel::* $maxLevel
*/ */
public function setAcceptedLevels(int|string|Level|array $minLevelOrList = Level::Debug, int|string|Level $maxLevel = Level::Emergency): self public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self
{ {
if (is_array($minLevelOrList)) { if (is_array($minLevelOrList)) {
$acceptedLevels = array_map(Logger::toMonologLevel(...), $minLevelOrList); $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList);
} else { } else {
$minLevelOrList = Logger::toMonologLevel($minLevelOrList); $minLevelOrList = Logger::toMonologLevel($minLevelOrList);
$maxLevel = Logger::toMonologLevel($maxLevel); $maxLevel = Logger::toMonologLevel($maxLevel);
$acceptedLevels = array_values(array_filter(Level::cases(), fn (Level $level) => $level->value >= $minLevelOrList->value && $level->value <= $maxLevel->value)); $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) {
} return $level >= $minLevelOrList && $level <= $maxLevel;
$this->acceptedLevels = []; }));
foreach ($acceptedLevels as $level) {
$this->acceptedLevels[$level->value] = true;
} }
$this->acceptedLevels = array_flip($acceptedLevels);
return $this; return $this;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return isset($this->acceptedLevels[$record->level->value]); return isset($this->acceptedLevels[$record['level']]);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (!$this->isHandling($record)) { if (!$this->isHandling($record)) {
return false; return false;
} }
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
@ -128,7 +136,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
@ -147,23 +155,26 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/** /**
* Return the nested handler * Return the nested handler
* *
* If the handler was provided as a factory, this will trigger the handler's instantiation. * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @return HandlerInterface
*
* @phpstan-param Record $record
*/ */
public function getHandler(LogRecord $record = null): HandlerInterface public function getHandler(array $record = null)
{ {
if (!$this->handler instanceof HandlerInterface) { if (!$this->handler instanceof HandlerInterface) {
$handler = ($this->handler)($record, $this); $this->handler = ($this->handler)($record, $this);
if (!$handler instanceof HandlerInterface) { if (!$this->handler instanceof HandlerInterface) {
throw new \RuntimeException("The factory Closure should return a HandlerInterface"); throw new \RuntimeException("The factory callable should return a HandlerInterface");
} }
$this->handler = $handler;
} }
return $this->handler; return $this->handler;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -178,7 +189,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {
@ -190,7 +201,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
} }
public function reset(): void public function reset()
{ {
$this->resetProcessors(); $this->resetProcessors();

View file

@ -11,17 +11,19 @@
namespace Monolog\Handler\FingersCrossed; namespace Monolog\Handler\FingersCrossed;
use Monolog\LogRecord;
/** /**
* Interface for activation strategies for the FingersCrossedHandler. * Interface for activation strategies for the FingersCrossedHandler.
* *
* @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
interface ActivationStrategyInterface interface ActivationStrategyInterface
{ {
/** /**
* Returns whether the given record activates the handler. * Returns whether the given record activates the handler.
*
* @phpstan-param Record $record
*/ */
public function isHandlerActivated(LogRecord $record): bool; public function isHandlerActivated(array $record): bool;
} }

View file

@ -11,10 +11,8 @@
namespace Monolog\Handler\FingersCrossed; namespace Monolog\Handler\FingersCrossed;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Channel and Error level based monolog activation strategy. Allows to trigger activation * Channel and Error level based monolog activation strategy. Allows to trigger activation
@ -25,45 +23,55 @@ use Monolog\LogRecord;
* *
* <code> * <code>
* $activationStrategy = new ChannelLevelActivationStrategy( * $activationStrategy = new ChannelLevelActivationStrategy(
* Level::Critical, * Logger::CRITICAL,
* array( * array(
* 'request' => Level::Alert, * 'request' => Logger::ALERT,
* 'sensitive' => Level::Error, * 'sensitive' => Logger::ERROR,
* ) * )
* ); * );
* $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy);
* </code> * </code>
* *
* @author Mike Meessen <netmikey@gmail.com> * @author Mike Meessen <netmikey@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class ChannelLevelActivationStrategy implements ActivationStrategyInterface class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{ {
private Level $defaultActionLevel; /**
* @var Level
*/
private $defaultActionLevel;
/** /**
* @var array<string, Level> * @var array<string, Level>
*/ */
private array $channelToActionLevel; private $channelToActionLevel;
/** /**
* @param int|string|Level|LogLevel::* $defaultActionLevel The default action level to be used if the record's category doesn't match any * @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any
* @param array<string, int|string|Level|LogLevel::*> $channelToActionLevel An array that maps channel names to action levels. * @param array<string, int> $channelToActionLevel An array that maps channel names to action levels.
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $defaultActionLevel * @phpstan-param array<string, Level> $channelToActionLevel
* @phpstan-param array<string, value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $channelToActionLevel * @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel
*/ */
public function __construct(int|string|Level $defaultActionLevel, array $channelToActionLevel = []) public function __construct($defaultActionLevel, array $channelToActionLevel = [])
{ {
$this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel);
$this->channelToActionLevel = array_map(Logger::toMonologLevel(...), $channelToActionLevel); $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel);
} }
public function isHandlerActivated(LogRecord $record): bool /**
* @phpstan-param Record $record
*/
public function isHandlerActivated(array $record): bool
{ {
if (isset($this->channelToActionLevel[$record->channel])) { if (isset($this->channelToActionLevel[$record['channel']])) {
return $record->level->value >= $this->channelToActionLevel[$record->channel]->value; return $record['level'] >= $this->channelToActionLevel[$record['channel']];
} }
return $record->level->value >= $this->defaultActionLevel->value; return $record['level'] >= $this->defaultActionLevel;
} }
} }

View file

@ -11,8 +11,6 @@
namespace Monolog\Handler\FingersCrossed; namespace Monolog\Handler\FingersCrossed;
use Monolog\Level;
use Monolog\LogRecord;
use Monolog\Logger; use Monolog\Logger;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
@ -20,23 +18,29 @@ use Psr\Log\LogLevel;
* Error level based activation strategy. * Error level based activation strategy.
* *
* @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class ErrorLevelActivationStrategy implements ActivationStrategyInterface class ErrorLevelActivationStrategy implements ActivationStrategyInterface
{ {
private Level $actionLevel; /**
* @var Level
*/
private $actionLevel;
/** /**
* @param int|string|Level $actionLevel Level or name or value * @param int|string $actionLevel Level or name or value
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $actionLevel * @phpstan-param Level|LevelName|LogLevel::* $actionLevel
*/ */
public function __construct(int|string|Level $actionLevel) public function __construct($actionLevel)
{ {
$this->actionLevel = Logger::toMonologLevel($actionLevel); $this->actionLevel = Logger::toMonologLevel($actionLevel);
} }
public function isHandlerActivated(LogRecord $record): bool public function isHandlerActivated(array $record): bool
{ {
return $record->level->value >= $this->actionLevel->value; return $record['level'] >= $this->actionLevel;
} }
} }

View file

@ -11,15 +11,12 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Closure;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Buffers all records until a certain level is reached * Buffers all records until a certain level is reached
@ -36,50 +33,55 @@ use Monolog\LogRecord;
* Monolog\Handler\FingersCrossed\ namespace. * Monolog\Handler\FingersCrossed\ namespace.
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{ {
use ProcessableHandlerTrait; use ProcessableHandlerTrait;
/** /**
* Handler or factory Closure($record, $this) * @var callable|HandlerInterface
* * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
* @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
*/ */
protected Closure|HandlerInterface $handler; protected $handler;
/** @var ActivationStrategyInterface */
protected ActivationStrategyInterface $activationStrategy; protected $activationStrategy;
/** @var bool */
protected bool $buffering = true; protected $buffering = true;
/** @var int */
protected int $bufferSize; protected $bufferSize;
/** @var Record[] */
/** @var LogRecord[] */ protected $buffer = [];
protected array $buffer = []; /** @var bool */
protected $stopBuffering;
protected bool $stopBuffering; /**
* @var ?int
protected Level|null $passthruLevel = null; * @phpstan-var ?Level
*/
protected bool $bubble; protected $passthruLevel;
/** @var bool */
protected $bubble;
/** /**
* @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
* *
* @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $fingersCrossedHandler). * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler).
* @param int|string|Level|LogLevel::* $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
* @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true)
* @param int|string|Level|LogLevel::*|null $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered * @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|ActivationStrategyInterface $activationStrategy * @phpstan-param Level|LevelName|LogLevel::* $passthruLevel
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $passthruLevel * @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy
*/ */
public function __construct(Closure|HandlerInterface $handler, int|string|Level|ActivationStrategyInterface $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, int|string|Level|null $passthruLevel = null) public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null)
{ {
if (null === $activationStrategy) { if (null === $activationStrategy) {
$activationStrategy = new ErrorLevelActivationStrategy(Level::Warning); $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
} }
// convert simple int activationStrategy to an object // convert simple int activationStrategy to an object
@ -96,12 +98,16 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
if ($passthruLevel !== null) { if ($passthruLevel !== null) {
$this->passthruLevel = Logger::toMonologLevel($passthruLevel); $this->passthruLevel = Logger::toMonologLevel($passthruLevel);
} }
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
}
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return true; return true;
} }
@ -120,11 +126,12 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
@ -144,7 +151,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
@ -153,7 +160,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
$this->getHandler()->close(); $this->getHandler()->close();
} }
public function reset(): void public function reset()
{ {
$this->flushBuffer(); $this->flushBuffer();
@ -183,7 +190,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
if (null !== $this->passthruLevel) { if (null !== $this->passthruLevel) {
$level = $this->passthruLevel; $level = $this->passthruLevel;
$this->buffer = array_filter($this->buffer, function ($record) use ($level) { $this->buffer = array_filter($this->buffer, function ($record) use ($level) {
return $record->level >= $level; return $record['level'] >= $level;
}); });
if (count($this->buffer) > 0) { if (count($this->buffer) > 0) {
$this->getHandler(end($this->buffer))->handleBatch($this->buffer); $this->getHandler(end($this->buffer))->handleBatch($this->buffer);
@ -197,23 +204,26 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
/** /**
* Return the nested handler * Return the nested handler
* *
* If the handler was provided as a factory, this will trigger the handler's instantiation. * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @return HandlerInterface
*
* @phpstan-param Record $record
*/ */
public function getHandler(LogRecord $record = null): HandlerInterface public function getHandler(array $record = null)
{ {
if (!$this->handler instanceof HandlerInterface) { if (!$this->handler instanceof HandlerInterface) {
$handler = ($this->handler)($record, $this); $this->handler = ($this->handler)($record, $this);
if (!$handler instanceof HandlerInterface) { if (!$this->handler instanceof HandlerInterface) {
throw new \RuntimeException("The factory Closure should return a HandlerInterface"); throw new \RuntimeException("The factory callable should return a HandlerInterface");
} }
$this->handler = $handler;
} }
return $this->handler; return $this->handler;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -228,7 +238,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {

View file

@ -13,12 +13,13 @@ namespace Monolog\Handler;
use Monolog\Formatter\WildfireFormatter; use Monolog\Formatter\WildfireFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
* *
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com> * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class FirePHPHandler extends AbstractProcessingHandler class FirePHPHandler extends AbstractProcessingHandler
{ {
@ -46,15 +47,18 @@ class FirePHPHandler extends AbstractProcessingHandler
/** /**
* Whether or not Wildfire vendor-specific headers have been generated & sent yet * Whether or not Wildfire vendor-specific headers have been generated & sent yet
* @var bool
*/ */
protected static bool $initialized = false; protected static $initialized = false;
/** /**
* Shared static message index between potentially multiple handlers * Shared static message index between potentially multiple handlers
* @var int
*/ */
protected static int $messageIndex = 1; protected static $messageIndex = 1;
protected static bool $sendHeaders = true; /** @var bool */
protected static $sendHeaders = true;
/** /**
* Base header creation function used by init headers & record headers * Base header creation function used by init headers & record headers
@ -81,19 +85,21 @@ class FirePHPHandler extends AbstractProcessingHandler
* @phpstan-return non-empty-array<string, string> * @phpstan-return non-empty-array<string, string>
* *
* @see createHeader() * @see createHeader()
*
* @phpstan-param FormattedRecord $record
*/ */
protected function createRecordHeader(LogRecord $record): array protected function createRecordHeader(array $record): array
{ {
// Wildfire is extensible to support multiple protocols & plugins in a single request, // Wildfire is extensible to support multiple protocols & plugins in a single request,
// but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
return $this->createHeader( return $this->createHeader(
[1, 1, 1, self::$messageIndex++], [1, 1, 1, self::$messageIndex++],
$record->formatted $record['formatted']
); );
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {
@ -134,7 +140,7 @@ class FirePHPHandler extends AbstractProcessingHandler
* @see sendHeader() * @see sendHeader()
* @see sendInitHeaders() * @see sendInitHeaders()
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!self::$sendHeaders || !$this->isWebRequest()) { if (!self::$sendHeaders || !$this->isWebRequest()) {
return; return;
@ -165,7 +171,7 @@ class FirePHPHandler extends AbstractProcessingHandler
*/ */
protected function headersAccepted(): bool protected function headersAccepted(): bool
{ {
if (isset($_SERVER['HTTP_USER_AGENT']) && 1 === preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) {
return true; return true;
} }

View file

@ -13,8 +13,7 @@ namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Sends logs to Fleep.io using Webhook integrations * Sends logs to Fleep.io using Webhook integrations
@ -23,6 +22,8 @@ use Monolog\LogRecord;
* *
* @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
* @author Ando Roots <ando@sqroot.eu> * @author Ando Roots <ando@sqroot.eu>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class FleepHookHandler extends SocketHandler class FleepHookHandler extends SocketHandler
{ {
@ -33,7 +34,7 @@ class FleepHookHandler extends SocketHandler
/** /**
* @var string Webhook token (specifies the conversation where logs are sent) * @var string Webhook token (specifies the conversation where logs are sent)
*/ */
protected string $token; protected $token;
/** /**
* Construct a new Fleep.io Handler. * Construct a new Fleep.io Handler.
@ -46,7 +47,7 @@ class FleepHookHandler extends SocketHandler
*/ */
public function __construct( public function __construct(
string $token, string $token,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
bool $persistent = false, bool $persistent = false,
float $timeout = 0.0, float $timeout = 0.0,
@ -88,16 +89,16 @@ class FleepHookHandler extends SocketHandler
/** /**
* Handles a log record * Handles a log record
*/ */
public function write(LogRecord $record): void public function write(array $record): void
{ {
parent::write($record); parent::write($record);
$this->closeSocket(); $this->closeSocket();
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
$content = $this->buildContent($record); $content = $this->buildContent($record);
@ -120,11 +121,13 @@ class FleepHookHandler extends SocketHandler
/** /**
* Builds the body of API call * Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/ */
private function buildContent(LogRecord $record): string private function buildContent(array $record): string
{ {
$dataArray = [ $dataArray = [
'message' => $record->formatted, 'message' => $record['formatted'],
]; ];
return http_build_query($dataArray); return http_build_query($dataArray);

View file

@ -11,11 +11,10 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\Formatter\FlowdockFormatter; use Monolog\Formatter\FlowdockFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Sends notifications through the Flowdock push API * Sends notifications through the Flowdock push API
@ -27,17 +26,22 @@ use Monolog\LogRecord;
* *
* @author Dominik Liebler <liebler.dominik@gmail.com> * @author Dominik Liebler <liebler.dominik@gmail.com>
* @see https://www.flowdock.com/api/push * @see https://www.flowdock.com/api/push
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class FlowdockHandler extends SocketHandler class FlowdockHandler extends SocketHandler
{ {
protected string $apiToken; /**
* @var string
*/
protected $apiToken;
/** /**
* @throws MissingExtensionException if OpenSSL is missing * @throws MissingExtensionException if OpenSSL is missing
*/ */
public function __construct( public function __construct(
string $apiToken, string $apiToken,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
bool $persistent = false, bool $persistent = false,
float $timeout = 0.0, float $timeout = 0.0,
@ -63,7 +67,7 @@ class FlowdockHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -83,9 +87,9 @@ class FlowdockHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
parent::write($record); parent::write($record);
@ -93,9 +97,9 @@ class FlowdockHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
$content = $this->buildContent($record); $content = $this->buildContent($record);
@ -104,10 +108,12 @@ class FlowdockHandler extends SocketHandler
/** /**
* Builds the body of API call * Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/ */
private function buildContent(LogRecord $record): string private function buildContent(array $record): string
{ {
return Utils::jsonEncode($record->formatted); return Utils::jsonEncode($record['formatted']['flowdock']);
} }
/** /**

View file

@ -23,12 +23,15 @@ interface FormattableHandlerInterface
/** /**
* Sets the formatter. * Sets the formatter.
* *
* @param FormatterInterface $formatter
* @return HandlerInterface self * @return HandlerInterface self
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface; public function setFormatter(FormatterInterface $formatter): HandlerInterface;
/** /**
* Gets the formatter. * Gets the formatter.
*
* @return FormatterInterface
*/ */
public function getFormatter(): FormatterInterface; public function getFormatter(): FormatterInterface;
} }

View file

@ -21,10 +21,13 @@ use Monolog\Formatter\LineFormatter;
*/ */
trait FormattableHandlerTrait trait FormattableHandlerTrait
{ {
protected FormatterInterface|null $formatter = null; /**
* @var ?FormatterInterface
*/
protected $formatter;
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -34,11 +37,11 @@ trait FormattableHandlerTrait
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {
if (null === $this->formatter) { if (!$this->formatter) {
$this->formatter = $this->getDefaultFormatter(); $this->formatter = $this->getDefaultFormatter();
} }

View file

@ -12,10 +12,9 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Gelf\PublisherInterface; use Gelf\PublisherInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\GelfMessageFormatter; use Monolog\Formatter\GelfMessageFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Handler to send messages to a Graylog2 (http://www.graylog2.org) server * Handler to send messages to a Graylog2 (http://www.graylog2.org) server
@ -28,12 +27,12 @@ class GelfHandler extends AbstractProcessingHandler
/** /**
* @var PublisherInterface the publisher object that sends the message to the server * @var PublisherInterface the publisher object that sends the message to the server
*/ */
protected PublisherInterface $publisher; protected $publisher;
/** /**
* @param PublisherInterface $publisher a gelf publisher object * @param PublisherInterface $publisher a gelf publisher object
*/ */
public function __construct(PublisherInterface $publisher, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -41,15 +40,15 @@ class GelfHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->publisher->publish($record->formatted); $this->publisher->publish($record['formatted']);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -13,20 +13,22 @@ namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Monolog\LogRecord;
/** /**
* Forwards records to multiple handlers * Forwards records to multiple handlers
* *
* @author Lenar Lõhmus <lenar@city.ee> * @author Lenar Lõhmus <lenar@city.ee>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
{ {
use ProcessableHandlerTrait; use ProcessableHandlerTrait;
/** @var HandlerInterface[] */ /** @var HandlerInterface[] */
protected array $handlers; protected $handlers;
protected bool $bubble; /** @var bool */
protected $bubble;
/** /**
* @param HandlerInterface[] $handlers Array of Handlers. * @param HandlerInterface[] $handlers Array of Handlers.
@ -45,9 +47,9 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
foreach ($this->handlers as $handler) { foreach ($this->handlers as $handler) {
if ($handler->isHandling($record)) { if ($handler->isHandling($record)) {
@ -59,11 +61,12 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
@ -75,15 +78,16 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
$processed = []; $processed = [];
foreach ($records as $record) { foreach ($records as $record) {
$processed[] = $this->processRecord($record); $processed[] = $this->processRecord($record);
} }
/** @var Record[] $records */
$records = $processed; $records = $processed;
} }
@ -92,7 +96,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
} }
} }
public function reset(): void public function reset()
{ {
$this->resetProcessors(); $this->resetProcessors();
@ -113,7 +117,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {

View file

@ -19,7 +19,7 @@ namespace Monolog\Handler;
abstract class Handler implements HandlerInterface abstract class Handler implements HandlerInterface
{ {
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
@ -29,7 +29,7 @@ abstract class Handler implements HandlerInterface
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {

View file

@ -11,12 +11,13 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\LogRecord;
/** /**
* Interface that all Monolog Handlers must implement * Interface that all Monolog Handlers must implement
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/ */
interface HandlerInterface interface HandlerInterface
{ {
@ -29,9 +30,13 @@ interface HandlerInterface
* is no guarantee that handle() will not be called, and isHandling() might not be called * is no guarantee that handle() will not be called, and isHandling() might not be called
* for a given record. * for a given record.
* *
* @param LogRecord $record Partial log record having only a level initialized * @param array $record Partial log record containing only a level key
*
* @return bool
*
* @phpstan-param array{level: Level} $record
*/ */
public function isHandling(LogRecord $record): bool; public function isHandling(array $record): bool;
/** /**
* Handles a record. * Handles a record.
@ -43,16 +48,20 @@ interface HandlerInterface
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
* calling further handlers in the stack with a given log record. * calling further handlers in the stack with a given log record.
* *
* @param LogRecord $record The record to handle * @param array $record The record to handle
* @return bool true means that this handler handled the record, and that bubbling is not permitted. * @return bool true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling. * false means the record was either not processed or that this handler allows bubbling.
*
* @phpstan-param Record $record
*/ */
public function handle(LogRecord $record): bool; public function handle(array $record): bool;
/** /**
* Handles a set of records at once. * Handles a set of records at once.
* *
* @param array<LogRecord> $records The records to handle * @param array $records The records to handle (an array of record arrays)
*
* @phpstan-param Record[] $records
*/ */
public function handleBatch(array $records): void; public function handleBatch(array $records): void;

View file

@ -13,7 +13,6 @@ namespace Monolog\Handler;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* This simple wrapper class can be used to extend handlers functionality. * This simple wrapper class can be used to extend handlers functionality.
@ -22,7 +21,7 @@ use Monolog\LogRecord;
* *
* Inherit from this class and override handle() like this: * Inherit from this class and override handle() like this:
* *
* public function handle(LogRecord $record) * public function handle(array $record)
* { * {
* if ($record meets certain conditions) { * if ($record meets certain conditions) {
* return false; * return false;
@ -34,7 +33,10 @@ use Monolog\LogRecord;
*/ */
class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface
{ {
protected HandlerInterface $handler; /**
* @var HandlerInterface
*/
protected $handler;
public function __construct(HandlerInterface $handler) public function __construct(HandlerInterface $handler)
{ {
@ -42,23 +44,23 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return $this->handler->isHandling($record); return $this->handler->isHandling($record);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
return $this->handler->handle($record); return $this->handler->handle($record);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
@ -66,7 +68,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
@ -74,7 +76,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function pushProcessor(callable $callback): HandlerInterface public function pushProcessor(callable $callback): HandlerInterface
{ {
@ -88,7 +90,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function popProcessor(): callable public function popProcessor(): callable
{ {
@ -100,7 +102,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -114,7 +116,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {
@ -125,7 +127,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
} }
public function reset(): void public function reset()
{ {
if ($this->handler instanceof ResettableInterface) { if ($this->handler instanceof ResettableInterface) {
$this->handler->reset(); $this->handler->reset();

View file

@ -11,9 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* IFTTTHandler uses cURL to trigger IFTTT Maker actions * IFTTTHandler uses cURL to trigger IFTTT Maker actions
@ -28,14 +27,16 @@ use Monolog\LogRecord;
*/ */
class IFTTTHandler extends AbstractProcessingHandler class IFTTTHandler extends AbstractProcessingHandler
{ {
private string $eventName; /** @var string */
private string $secretKey; private $eventName;
/** @var string */
private $secretKey;
/** /**
* @param string $eventName The name of the IFTTT Maker event that should be triggered * @param string $eventName The name of the IFTTT Maker event that should be triggered
* @param string $secretKey A valid IFTTT secret key * @param string $secretKey A valid IFTTT secret key
*/ */
public function __construct(string $eventName, string $secretKey, int|string|Level $level = Level::Error, bool $bubble = true) public function __construct(string $eventName, string $secretKey, $level = Logger::ERROR, bool $bubble = true)
{ {
if (!extension_loaded('curl')) { if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler'); throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler');
@ -48,14 +49,14 @@ class IFTTTHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function write(LogRecord $record): void public function write(array $record): void
{ {
$postData = [ $postData = [
"value1" => $record->channel, "value1" => $record["channel"],
"value2" => $record["level_name"], "value2" => $record["level_name"],
"value3" => $record->message, "value3" => $record["message"],
]; ];
$postString = Utils::jsonEncode($postData); $postString = Utils::jsonEncode($postData);

View file

@ -11,8 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Inspired on LogEntriesHandler. * Inspired on LogEntriesHandler.
@ -22,7 +21,10 @@ use Monolog\LogRecord;
*/ */
class InsightOpsHandler extends SocketHandler class InsightOpsHandler extends SocketHandler
{ {
protected string $logToken; /**
* @var string
*/
protected $logToken;
/** /**
* @param string $token Log token supplied by InsightOps * @param string $token Log token supplied by InsightOps
@ -35,7 +37,7 @@ class InsightOpsHandler extends SocketHandler
string $token, string $token,
string $region = 'us', string $region = 'us',
bool $useSSL = true, bool $useSSL = true,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
bool $persistent = false, bool $persistent = false,
float $timeout = 0.0, float $timeout = 0.0,
@ -65,10 +67,10 @@ class InsightOpsHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
return $this->logToken . ' ' . $record->formatted; return $this->logToken . ' ' . $record['formatted'];
} }
} }

View file

@ -11,15 +11,17 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* @author Robert Kaufmann III <rok3@rok3.me> * @author Robert Kaufmann III <rok3@rok3.me>
*/ */
class LogEntriesHandler extends SocketHandler class LogEntriesHandler extends SocketHandler
{ {
protected string $logToken; /**
* @var string
*/
protected $logToken;
/** /**
* @param string $token Log token supplied by LogEntries * @param string $token Log token supplied by LogEntries
@ -31,7 +33,7 @@ class LogEntriesHandler extends SocketHandler
public function __construct( public function __construct(
string $token, string $token,
bool $useSSL = true, bool $useSSL = true,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
string $host = 'data.logentries.com', string $host = 'data.logentries.com',
bool $persistent = false, bool $persistent = false,
@ -59,10 +61,10 @@ class LogEntriesHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
return $this->logToken . ' ' . $record->formatted; return $this->logToken . ' ' . $record['formatted'];
} }
} }

View file

@ -11,12 +11,11 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LogglyFormatter; use Monolog\Formatter\LogglyFormatter;
use function array_key_exists; use function array_key_exists;
use CurlHandle; use CurlHandle;
use Monolog\LogRecord;
/** /**
* Sends errors to Loggly. * Sends errors to Loggly.
@ -34,21 +33,22 @@ class LogglyHandler extends AbstractProcessingHandler
/** /**
* Caches the curl handlers for every given endpoint. * Caches the curl handlers for every given endpoint.
* *
* @var CurlHandle[] * @var resource[]|CurlHandle[]
*/ */
protected array $curlHandlers = []; protected $curlHandlers = [];
protected string $token; /** @var string */
protected $token;
/** @var string[] */ /** @var string[] */
protected array $tag = []; protected $tag = [];
/** /**
* @param string $token API token supplied by Loggly * @param string $token API token supplied by Loggly
* *
* @throws MissingExtensionException If the curl extension is missing * @throws MissingExtensionException If the curl extension is missing
*/ */
public function __construct(string $token, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true)
{ {
if (!extension_loaded('curl')) { if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler'); throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler');
@ -61,8 +61,12 @@ class LogglyHandler extends AbstractProcessingHandler
/** /**
* Loads and returns the shared curl handler for the given endpoint. * Loads and returns the shared curl handler for the given endpoint.
*
* @param string $endpoint
*
* @return resource|CurlHandle
*/ */
protected function getCurlHandler(string $endpoint): CurlHandle protected function getCurlHandler(string $endpoint)
{ {
if (!array_key_exists($endpoint, $this->curlHandlers)) { if (!array_key_exists($endpoint, $this->curlHandlers)) {
$this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint); $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint);
@ -73,8 +77,12 @@ class LogglyHandler extends AbstractProcessingHandler
/** /**
* Starts a fresh curl session for the given endpoint and returns its handler. * Starts a fresh curl session for the given endpoint and returns its handler.
*
* @param string $endpoint
*
* @return resource|CurlHandle
*/ */
private function loadCurlHandle(string $endpoint): CurlHandle private function loadCurlHandle(string $endpoint)
{ {
$url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token); $url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token);
@ -90,13 +98,10 @@ class LogglyHandler extends AbstractProcessingHandler
/** /**
* @param string[]|string $tag * @param string[]|string $tag
*/ */
public function setTag(string|array $tag): self public function setTag($tag): self
{ {
if ('' === $tag || [] === $tag) { $tag = !empty($tag) ? $tag : [];
$this->tag = [];
} else {
$this->tag = is_array($tag) ? $tag : [$tag]; $this->tag = is_array($tag) ? $tag : [$tag];
}
return $this; return $this;
} }
@ -104,9 +109,9 @@ class LogglyHandler extends AbstractProcessingHandler
/** /**
* @param string[]|string $tag * @param string[]|string $tag
*/ */
public function addTag(string|array $tag): self public function addTag($tag): self
{ {
if ('' !== $tag) { if (!empty($tag)) {
$tag = is_array($tag) ? $tag : [$tag]; $tag = is_array($tag) ? $tag : [$tag];
$this->tag = array_unique(array_merge($this->tag, $tag)); $this->tag = array_unique(array_merge($this->tag, $tag));
} }
@ -114,9 +119,9 @@ class LogglyHandler extends AbstractProcessingHandler
return $this; return $this;
} }
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->send($record->formatted, static::ENDPOINT_SINGLE); $this->send($record["formatted"], static::ENDPOINT_SINGLE);
} }
public function handleBatch(array $records): void public function handleBatch(array $records): void
@ -124,10 +129,10 @@ class LogglyHandler extends AbstractProcessingHandler
$level = $this->level; $level = $this->level;
$records = array_filter($records, function ($record) use ($level) { $records = array_filter($records, function ($record) use ($level) {
return ($record->level >= $level); return ($record['level'] >= $level);
}); });
if (\count($records) > 0) { if ($records) {
$this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH); $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH);
} }
} }
@ -138,7 +143,7 @@ class LogglyHandler extends AbstractProcessingHandler
$headers = ['Content-Type: application/json']; $headers = ['Content-Type: application/json'];
if (\count($this->tag) > 0) { if (!empty($this->tag)) {
$headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag);
} }

View file

@ -11,26 +11,34 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LogmaticFormatter; use Monolog\Formatter\LogmaticFormatter;
use Monolog\LogRecord;
/** /**
* @author Julien Breux <julien.breux@gmail.com> * @author Julien Breux <julien.breux@gmail.com>
*/ */
class LogmaticHandler extends SocketHandler class LogmaticHandler extends SocketHandler
{ {
private string $logToken; /**
* @var string
*/
private $logToken;
private string $hostname; /**
* @var string
*/
private $hostname;
private string $appName; /**
* @var string
*/
private $appname;
/** /**
* @param string $token Log token supplied by Logmatic. * @param string $token Log token supplied by Logmatic.
* @param string $hostname Host name supplied by Logmatic. * @param string $hostname Host name supplied by Logmatic.
* @param string $appName Application name supplied by Logmatic. * @param string $appname Application name supplied by Logmatic.
* @param bool $useSSL Whether or not SSL encryption should be used. * @param bool $useSSL Whether or not SSL encryption should be used.
* *
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
@ -38,9 +46,9 @@ class LogmaticHandler extends SocketHandler
public function __construct( public function __construct(
string $token, string $token,
string $hostname = '', string $hostname = '',
string $appName = '', string $appname = '',
bool $useSSL = true, bool $useSSL = true,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
bool $persistent = false, bool $persistent = false,
float $timeout = 0.0, float $timeout = 0.0,
@ -68,29 +76,29 @@ class LogmaticHandler extends SocketHandler
$this->logToken = $token; $this->logToken = $token;
$this->hostname = $hostname; $this->hostname = $hostname;
$this->appName = $appName; $this->appname = $appname;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
return $this->logToken . ' ' . $record->formatted; return $this->logToken . ' ' . $record['formatted'];
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {
$formatter = new LogmaticFormatter(); $formatter = new LogmaticFormatter();
if ($this->hostname !== '') { if (!empty($this->hostname)) {
$formatter->setHostname($this->hostname); $formatter->setHostname($this->hostname);
} }
if ($this->appName !== '') { if (!empty($this->appname)) {
$formatter->setAppName($this->appName); $formatter->setAppname($this->appname);
} }
return $formatter; return $formatter;

View file

@ -13,32 +13,33 @@ namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\HtmlFormatter; use Monolog\Formatter\HtmlFormatter;
use Monolog\LogRecord;
/** /**
* Base class for all mail handlers * Base class for all mail handlers
* *
* @author Gyula Sallai * @author Gyula Sallai
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
abstract class MailHandler extends AbstractProcessingHandler abstract class MailHandler extends AbstractProcessingHandler
{ {
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
$messages = []; $messages = [];
foreach ($records as $record) { foreach ($records as $record) {
if ($record->level->isLowerThan($this->level)) { if ($record['level'] < $this->level) {
continue; continue;
} }
/** @var Record $message */
$message = $this->processRecord($record); $message = $this->processRecord($record);
$messages[] = $message; $messages[] = $message;
} }
if (\count($messages) > 0) { if (!empty($messages)) {
$this->send((string) $this->getFormatter()->formatBatch($messages), $messages); $this->send((string) $this->getFormatter()->formatBatch($messages), $messages);
} }
} }
@ -49,26 +50,27 @@ abstract class MailHandler extends AbstractProcessingHandler
* @param string $content formatted email body to be sent * @param string $content formatted email body to be sent
* @param array $records the array of log records that formed this content * @param array $records the array of log records that formed this content
* *
* @phpstan-param non-empty-array<LogRecord> $records * @phpstan-param Record[] $records
*/ */
abstract protected function send(string $content, array $records): void; abstract protected function send(string $content, array $records): void;
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->send((string) $record->formatted, [$record]); $this->send((string) $record['formatted'], [$record]);
} }
/** /**
* @phpstan-param non-empty-array<LogRecord> $records * @phpstan-param non-empty-array<Record> $records
* @phpstan-return Record
*/ */
protected function getHighestRecord(array $records): LogRecord protected function getHighestRecord(array $records): array
{ {
$highestRecord = null; $highestRecord = null;
foreach ($records as $record) { foreach ($records as $record) {
if ($highestRecord === null || $record->level->isHigherThan($highestRecord->level)) { if ($highestRecord === null || $highestRecord['level'] < $record['level']) {
$highestRecord = $record; $highestRecord = $record;
} }
} }
@ -83,6 +85,8 @@ abstract class MailHandler extends AbstractProcessingHandler
/** /**
* Gets the default formatter. * Gets the default formatter.
*
* @return FormatterInterface
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,7 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Swift; use Swift;
use Swift_Message; use Swift_Message;
@ -22,20 +22,22 @@ use Swift_Message;
*/ */
class MandrillHandler extends MailHandler class MandrillHandler extends MailHandler
{ {
protected Swift_Message $message; /** @var Swift_Message */
protected string $apiKey; protected $message;
/** @var string */
protected $apiKey;
/** /**
* @phpstan-param (Swift_Message|callable(): Swift_Message) $message * @psalm-param Swift_Message|callable(): Swift_Message $message
* *
* @param string $apiKey A valid Mandrill API key * @param string $apiKey A valid Mandrill API key
* @param callable|Swift_Message $message An example message for real messages, only the body will be replaced * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
*/ */
public function __construct(string $apiKey, callable|Swift_Message $message, int|string|Level $level = Level::Error, bool $bubble = true) public function __construct(string $apiKey, $message, $level = Logger::ERROR, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
if (!$message instanceof Swift_Message) { if (!$message instanceof Swift_Message && is_callable($message)) {
$message = $message(); $message = $message();
} }
if (!$message instanceof Swift_Message) { if (!$message instanceof Swift_Message) {
@ -46,7 +48,7 @@ class MandrillHandler extends MailHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function send(string $content, array $records): void protected function send(string $content, array $records): void
{ {

View file

@ -14,10 +14,9 @@ namespace Monolog\Handler;
use MongoDB\Driver\BulkWrite; use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Manager; use MongoDB\Driver\Manager;
use MongoDB\Client; use MongoDB\Client;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\MongoDBFormatter; use Monolog\Formatter\MongoDBFormatter;
use Monolog\LogRecord;
/** /**
* Logs to a MongoDB database. * Logs to a MongoDB database.
@ -34,11 +33,12 @@ use Monolog\LogRecord;
*/ */
class MongoDBHandler extends AbstractProcessingHandler class MongoDBHandler extends AbstractProcessingHandler
{ {
private \MongoDB\Collection $collection; /** @var \MongoDB\Collection */
private $collection;
private Client|Manager $manager; /** @var Client|Manager */
private $manager;
private string|null $namespace = null; /** @var string */
private $namespace;
/** /**
* Constructor. * Constructor.
@ -47,8 +47,12 @@ class MongoDBHandler extends AbstractProcessingHandler
* @param string $database Database name * @param string $database Database name
* @param string $collection Collection name * @param string $collection Collection name
*/ */
public function __construct(Client|Manager $mongodb, string $database, string $collection, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($mongodb, string $database, string $collection, $level = Logger::DEBUG, bool $bubble = true)
{ {
if (!($mongodb instanceof Client || $mongodb instanceof Manager)) {
throw new \InvalidArgumentException('MongoDB\Client or MongoDB\Driver\Manager instance required');
}
if ($mongodb instanceof Client) { if ($mongodb instanceof Client) {
$this->collection = $mongodb->selectCollection($database, $collection); $this->collection = $mongodb->selectCollection($database, $collection);
} else { } else {
@ -59,21 +63,21 @@ class MongoDBHandler extends AbstractProcessingHandler
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
} }
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (isset($this->collection)) { if (isset($this->collection)) {
$this->collection->insertOne($record->formatted); $this->collection->insertOne($record['formatted']);
} }
if (isset($this->manager, $this->namespace)) { if (isset($this->manager, $this->namespace)) {
$bulk = new BulkWrite; $bulk = new BulkWrite;
$bulk->insert($record->formatted); $bulk->insert($record["formatted"]);
$this->manager->executeBulkWrite($this->namespace, $bulk); $this->manager->executeBulkWrite($this->namespace, $bulk);
} }
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,7 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
/** /**
@ -26,39 +26,43 @@ class NativeMailerHandler extends MailHandler
* The email addresses to which the message will be sent * The email addresses to which the message will be sent
* @var string[] * @var string[]
*/ */
protected array $to; protected $to;
/** /**
* The subject of the email * The subject of the email
* @var string
*/ */
protected string $subject; protected $subject;
/** /**
* Optional headers for the message * Optional headers for the message
* @var string[] * @var string[]
*/ */
protected array $headers = []; protected $headers = [];
/** /**
* Optional parameters for the message * Optional parameters for the message
* @var string[] * @var string[]
*/ */
protected array $parameters = []; protected $parameters = [];
/** /**
* The wordwrap length for the message * The wordwrap length for the message
* @var int
*/ */
protected int $maxColumnWidth; protected $maxColumnWidth;
/** /**
* The Content-type for the message * The Content-type for the message
* @var string|null
*/ */
protected string|null $contentType = null; protected $contentType;
/** /**
* The encoding for the message * The encoding for the message
* @var string
*/ */
protected string $encoding = 'utf-8'; protected $encoding = 'utf-8';
/** /**
* @param string|string[] $to The receiver of the mail * @param string|string[] $to The receiver of the mail
@ -66,7 +70,7 @@ class NativeMailerHandler extends MailHandler
* @param string $from The sender of the mail * @param string $from The sender of the mail
* @param int $maxColumnWidth The maximum column width that the message lines will have * @param int $maxColumnWidth The maximum column width that the message lines will have
*/ */
public function __construct(string|array $to, string $subject, string $from, int|string|Level $level = Level::Error, bool $bubble = true, int $maxColumnWidth = 70) public function __construct($to, string $subject, string $from, $level = Logger::ERROR, bool $bubble = true, int $maxColumnWidth = 70)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->to = (array) $to; $this->to = (array) $to;
@ -105,11 +109,11 @@ class NativeMailerHandler extends MailHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function send(string $content, array $records): void protected function send(string $content, array $records): void
{ {
$contentType = $this->getContentType() ?? ($this->isHtmlBody($content) ? 'text/html' : 'text/plain'); $contentType = $this->getContentType() ?: ($this->isHtmlBody($content) ? 'text/html' : 'text/plain');
if ($contentType !== 'text/html') { if ($contentType !== 'text/html') {
$content = wordwrap($content, $this->maxColumnWidth); $content = wordwrap($content, $this->maxColumnWidth);
@ -121,8 +125,11 @@ class NativeMailerHandler extends MailHandler
$headers .= 'MIME-Version: 1.0' . "\r\n"; $headers .= 'MIME-Version: 1.0' . "\r\n";
} }
$subject = $this->subject;
if ($records) {
$subjectFormatter = new LineFormatter($this->subject); $subjectFormatter = new LineFormatter($this->subject);
$subject = $subjectFormatter->format($this->getHighestRecord($records)); $subject = $subjectFormatter->format($this->getHighestRecord($records));
}
$parameters = implode(' ', $this->parameters); $parameters = implode(' ', $this->parameters);
foreach ($this->to as $to) { foreach ($this->to as $to) {

View file

@ -11,17 +11,16 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\Formatter\NormalizerFormatter; use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Class to record a log on a NewRelic application. * Class to record a log on a NewRelic application.
* Enabling New Relic High Security mode may prevent capture of useful information. * Enabling New Relic High Security mode may prevent capture of useful information.
* *
* This handler requires a NormalizerFormatter to function and expects an array in $record->formatted * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted']
* *
* @see https://docs.newrelic.com/docs/agents/php-agent * @see https://docs.newrelic.com/docs/agents/php-agent
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
@ -29,58 +28,75 @@ use Monolog\LogRecord;
class NewRelicHandler extends AbstractProcessingHandler class NewRelicHandler extends AbstractProcessingHandler
{ {
/** /**
* @inheritDoc * Name of the New Relic application that will receive logs from this handler.
*
* @var ?string
*/ */
public function __construct( protected $appName;
int|string|Level $level = Level::Error,
bool $bubble = true,
/** /**
* Name of the New Relic application that will receive logs from this handler. * Name of the current transaction
*
* @var ?string
*/ */
protected string|null $appName = null, protected $transactionName;
/** /**
* Some context and extra data is passed into the handler as arrays of values. Do we send them as is * Some context and extra data is passed into the handler as arrays of values. Do we send them as is
* (useful if we are using the API), or explode them for display on the NewRelic RPM website? * (useful if we are using the API), or explode them for display on the NewRelic RPM website?
*
* @var bool
*/ */
protected bool $explodeArrays = false, protected $explodeArrays;
/** /**
* Name of the current transaction * {@inheritDoc}
*
* @param string|null $appName
* @param bool $explodeArrays
* @param string|null $transactionName
*/ */
protected string|null $transactionName = null public function __construct(
$level = Logger::ERROR,
bool $bubble = true,
?string $appName = null,
bool $explodeArrays = false,
?string $transactionName = null
) { ) {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->appName = $appName;
$this->explodeArrays = $explodeArrays;
$this->transactionName = $transactionName;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!$this->isNewRelicEnabled()) { if (!$this->isNewRelicEnabled()) {
throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler');
} }
if (null !== ($appName = $this->getAppName($record->context))) { if ($appName = $this->getAppName($record['context'])) {
$this->setNewRelicAppName($appName); $this->setNewRelicAppName($appName);
} }
if (null !== ($transactionName = $this->getTransactionName($record->context))) { if ($transactionName = $this->getTransactionName($record['context'])) {
$this->setNewRelicTransactionName($transactionName); $this->setNewRelicTransactionName($transactionName);
unset($record->formatted['context']['transaction_name']); unset($record['formatted']['context']['transaction_name']);
} }
if (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) { if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
newrelic_notice_error($record->message, $record->context['exception']); newrelic_notice_error($record['message'], $record['context']['exception']);
unset($record->formatted['context']['exception']); unset($record['formatted']['context']['exception']);
} else { } else {
newrelic_notice_error($record->message); newrelic_notice_error($record['message']);
} }
if (isset($record->formatted['context']) && is_array($record->formatted['context'])) { if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) {
foreach ($record->formatted['context'] as $key => $parameter) { foreach ($record['formatted']['context'] as $key => $parameter) {
if (is_array($parameter) && $this->explodeArrays) { if (is_array($parameter) && $this->explodeArrays) {
foreach ($parameter as $paramKey => $paramValue) { foreach ($parameter as $paramKey => $paramValue) {
$this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue);
@ -91,8 +107,8 @@ class NewRelicHandler extends AbstractProcessingHandler
} }
} }
if (isset($record->formatted['extra']) && is_array($record->formatted['extra'])) { if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) {
foreach ($record->formatted['extra'] as $key => $parameter) { foreach ($record['formatted']['extra'] as $key => $parameter) {
if (is_array($parameter) && $this->explodeArrays) { if (is_array($parameter) && $this->explodeArrays) {
foreach ($parameter as $paramKey => $paramValue) { foreach ($parameter as $paramKey => $paramValue) {
$this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue);
@ -106,6 +122,8 @@ class NewRelicHandler extends AbstractProcessingHandler
/** /**
* Checks whether the NewRelic extension is enabled in the system. * Checks whether the NewRelic extension is enabled in the system.
*
* @return bool
*/ */
protected function isNewRelicEnabled(): bool protected function isNewRelicEnabled(): bool
{ {
@ -159,6 +177,7 @@ class NewRelicHandler extends AbstractProcessingHandler
} }
/** /**
* @param string $key
* @param mixed $value * @param mixed $value
*/ */
protected function setNewRelicParameter(string $key, $value): void protected function setNewRelicParameter(string $key, $value): void
@ -171,7 +190,7 @@ class NewRelicHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,8 +11,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\LogRecord;
/** /**
* No-op * No-op
* *
@ -25,17 +23,17 @@ use Monolog\LogRecord;
class NoopHandler extends Handler class NoopHandler extends Handler
{ {
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return true; return true;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
return false; return false;
} }

View file

@ -11,10 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level;
use Psr\Log\LogLevel;
use Monolog\Logger; use Monolog\Logger;
use Monolog\LogRecord; use Psr\Log\LogLevel;
/** /**
* Blackhole * Blackhole
@ -23,34 +21,40 @@ use Monolog\LogRecord;
* to put on top of an existing stack to override it temporarily. * to put on top of an existing stack to override it temporarily.
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class NullHandler extends Handler class NullHandler extends Handler
{ {
private Level $level; /**
* @var int
*/
private $level;
/** /**
* @param string|int|Level $level The minimum logging level at which this handler will be triggered * @param string|int $level The minimum logging level at which this handler will be triggered
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level * @phpstan-param Level|LevelName|LogLevel::* $level
*/ */
public function __construct(string|int|Level $level = Level::Debug) public function __construct($level = Logger::DEBUG)
{ {
$this->level = Logger::toMonologLevel($level); $this->level = Logger::toMonologLevel($level);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return $record->level->value >= $this->level->value; return $record['level'] >= $this->level;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
return $record->level->value >= $this->level->value; return $record['level'] >= $this->level;
} }
} }

View file

@ -11,9 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Handler to only pass log messages when a certain threshold of number of messages is reached. * Handler to only pass log messages when a certain threshold of number of messages is reached.
@ -28,7 +27,7 @@ use Monolog\LogRecord;
* $handler = new SomeHandler(...) * $handler = new SomeHandler(...)
* *
* // Pass all warnings to the handler when more than 10 & all error messages when more then 5 * // Pass all warnings to the handler when more than 10 & all error messages when more then 5
* $overflow = new OverflowHandler($handler, [Level::Warning->value => 10, Level::Error->value => 5]); * $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]);
* *
* $log->pushHandler($overflow); * $log->pushHandler($overflow);
*``` *```
@ -37,25 +36,36 @@ use Monolog\LogRecord;
*/ */
class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface
{ {
private HandlerInterface $handler; /** @var HandlerInterface */
private $handler;
/** @var array<int, int> */ /** @var int[] */
private array $thresholdMap = []; private $thresholdMap = [
Logger::DEBUG => 0,
Logger::INFO => 0,
Logger::NOTICE => 0,
Logger::WARNING => 0,
Logger::ERROR => 0,
Logger::CRITICAL => 0,
Logger::ALERT => 0,
Logger::EMERGENCY => 0,
];
/** /**
* Buffer of all messages passed to the handler before the threshold was reached * Buffer of all messages passed to the handler before the threshold was reached
* *
* @var mixed[][] * @var mixed[][]
*/ */
private array $buffer = []; private $buffer = [];
/** /**
* @param array<int, int> $thresholdMap Dictionary of log level value => threshold * @param HandlerInterface $handler
* @param int[] $thresholdMap Dictionary of logger level => threshold
*/ */
public function __construct( public function __construct(
HandlerInterface $handler, HandlerInterface $handler,
array $thresholdMap = [], array $thresholdMap = [],
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true bool $bubble = true
) { ) {
$this->handler = $handler; $this->handler = $handler;
@ -75,15 +85,15 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
* calling further handlers in the stack with a given log record. * calling further handlers in the stack with a given log record.
* *
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if ($record->level->isLowerThan($this->level)) { if ($record['level'] < $this->level) {
return false; return false;
} }
$level = $record->level->value; $level = $record['level'];
if (!isset($this->thresholdMap[$level])) { if (!isset($this->thresholdMap[$level])) {
$this->thresholdMap[$level] = 0; $this->thresholdMap[$level] = 0;
@ -112,7 +122,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -126,7 +136,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {

View file

@ -13,13 +13,11 @@ namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use PhpConsole\Connector; use PhpConsole\Connector;
use PhpConsole\Handler as VendorPhpConsoleHandler; use PhpConsole\Handler as VendorPhpConsoleHandler;
use PhpConsole\Helper; use PhpConsole\Helper;
use Monolog\LogRecord;
use PhpConsole\Storage;
/** /**
* Monolog handler for Google Chrome extension "PHP Console" * Monolog handler for Google Chrome extension "PHP Console"
@ -39,59 +37,14 @@ use PhpConsole\Storage;
* PC::debug($_SERVER); // PHP Console debugger for any type of vars * PC::debug($_SERVER); // PHP Console debugger for any type of vars
* *
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin * @author Sergey Barbushin https://www.linkedin.com/in/barbushin
* @phpstan-type Options array{
* enabled: bool,
* classesPartialsTraceIgnore: string[],
* debugTagsKeysInContext: array<int|string>,
* useOwnErrorsHandler: bool,
* useOwnExceptionsHandler: bool,
* sourcesBasePath: string|null,
* registerHelper: bool,
* serverEncoding: string|null,
* headersLimit: int|null,
* password: string|null,
* enableSslOnlyMode: bool,
* ipMasks: string[],
* enableEvalListener: bool,
* dumperDetectCallbacks: bool,
* dumperLevelLimit: int,
* dumperItemsCountLimit: int,
* dumperItemSizeLimit: int,
* dumperDumpSizeLimit: int,
* detectDumpTraceAndSource: bool,
* dataStorage: Storage|null
* }
* @phpstan-type InputOptions array{
* enabled?: bool,
* classesPartialsTraceIgnore?: string[],
* debugTagsKeysInContext?: array<int|string>,
* useOwnErrorsHandler?: bool,
* useOwnExceptionsHandler?: bool,
* sourcesBasePath?: string|null,
* registerHelper?: bool,
* serverEncoding?: string|null,
* headersLimit?: int|null,
* password?: string|null,
* enableSslOnlyMode?: bool,
* ipMasks?: string[],
* enableEvalListener?: bool,
* dumperDetectCallbacks?: bool,
* dumperLevelLimit?: int,
* dumperItemsCountLimit?: int,
* dumperItemSizeLimit?: int,
* dumperDumpSizeLimit?: int,
* detectDumpTraceAndSource?: bool,
* dataStorage?: Storage|null
* }
* *
* @phpstan-import-type Record from \Monolog\Logger
* @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4 * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4
*/ */
class PHPConsoleHandler extends AbstractProcessingHandler class PHPConsoleHandler extends AbstractProcessingHandler
{ {
/** /** @var array<string, mixed> */
* @phpstan-var Options private $options = [
*/
private array $options = [
'enabled' => true, // bool Is PHP Console server enabled 'enabled' => true, // bool Is PHP Console server enabled
'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with... 'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled 'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled
@ -114,15 +67,15 @@ class PHPConsoleHandler extends AbstractProcessingHandler
'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) 'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ)
]; ];
private Connector $connector; /** @var Connector */
private $connector;
/** /**
* @param array<string, mixed> $options See \Monolog\Handler\PHPConsoleHandler::$options for more details * @param array<string, mixed> $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
* @throws \RuntimeException * @throws \RuntimeException
* @phpstan-param InputOptions $options
*/ */
public function __construct(array $options = [], ?Connector $connector = null, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true)
{ {
if (!class_exists('PhpConsole\Connector')) { if (!class_exists('PhpConsole\Connector')) {
throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
@ -134,15 +87,13 @@ class PHPConsoleHandler extends AbstractProcessingHandler
/** /**
* @param array<string, mixed> $options * @param array<string, mixed> $options
* @return array<string, mixed>
* *
* @phpstan-param InputOptions $options * @return array<string, mixed>
* @phpstan-return Options
*/ */
private function initOptions(array $options): array private function initOptions(array $options): array
{ {
$wrongOptions = array_diff(array_keys($options), array_keys($this->options)); $wrongOptions = array_diff(array_keys($options), array_keys($this->options));
if (\count($wrongOptions) > 0) { if ($wrongOptions) {
throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions)); throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions));
} }
@ -151,8 +102,8 @@ class PHPConsoleHandler extends AbstractProcessingHandler
private function initConnector(?Connector $connector = null): Connector private function initConnector(?Connector $connector = null): Connector
{ {
if (null === $connector) { if (!$connector) {
if ($this->options['dataStorage'] instanceof Storage) { if ($this->options['dataStorage']) {
Connector::setPostponeStorage($this->options['dataStorage']); Connector::setPostponeStorage($this->options['dataStorage']);
} }
$connector = Connector::getInstance(); $connector = Connector::getInstance();
@ -169,22 +120,22 @@ class PHPConsoleHandler extends AbstractProcessingHandler
$handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']);
$handler->start(); $handler->start();
} }
if (null !== $this->options['sourcesBasePath']) { if ($this->options['sourcesBasePath']) {
$connector->setSourcesBasePath($this->options['sourcesBasePath']); $connector->setSourcesBasePath($this->options['sourcesBasePath']);
} }
if (null !== $this->options['serverEncoding']) { if ($this->options['serverEncoding']) {
$connector->setServerEncoding($this->options['serverEncoding']); $connector->setServerEncoding($this->options['serverEncoding']);
} }
if (null !== $this->options['password']) { if ($this->options['password']) {
$connector->setPassword($this->options['password']); $connector->setPassword($this->options['password']);
} }
if ($this->options['enableSslOnlyMode']) { if ($this->options['enableSslOnlyMode']) {
$connector->enableSslOnlyMode(); $connector->enableSslOnlyMode();
} }
if (\count($this->options['ipMasks']) > 0) { if ($this->options['ipMasks']) {
$connector->setAllowedIpMasks($this->options['ipMasks']); $connector->setAllowedIpMasks($this->options['ipMasks']);
} }
if (null !== $this->options['headersLimit'] && $this->options['headersLimit'] > 0) { if ($this->options['headersLimit']) {
$connector->setHeadersLimit($this->options['headersLimit']); $connector->setHeadersLimit($this->options['headersLimit']);
} }
if ($this->options['detectDumpTraceAndSource']) { if ($this->options['detectDumpTraceAndSource']) {
@ -217,7 +168,7 @@ class PHPConsoleHandler extends AbstractProcessingHandler
return $this->options; return $this->options;
} }
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if ($this->options['enabled'] && $this->connector->isActiveClient()) { if ($this->options['enabled'] && $this->connector->isActiveClient()) {
return parent::handle($record); return parent::handle($record);
@ -229,39 +180,48 @@ class PHPConsoleHandler extends AbstractProcessingHandler
/** /**
* Writes the record down to the log of the implementing handler * Writes the record down to the log of the implementing handler
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if ($record->level->isLowerThan(Level::Notice)) { if ($record['level'] < Logger::NOTICE) {
$this->handleDebugRecord($record); $this->handleDebugRecord($record);
} elseif (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) { } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
$this->handleExceptionRecord($record); $this->handleExceptionRecord($record);
} else { } else {
$this->handleErrorRecord($record); $this->handleErrorRecord($record);
} }
} }
private function handleDebugRecord(LogRecord $record): void /**
* @phpstan-param Record $record
*/
private function handleDebugRecord(array $record): void
{ {
[$tags, $filteredContext] = $this->getRecordTags($record); $tags = $this->getRecordTags($record);
$message = $record->message; $message = $record['message'];
if (\count($filteredContext) > 0) { if ($record['context']) {
$message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($filteredContext)), null, true); $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true);
} }
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
} }
private function handleExceptionRecord(LogRecord $record): void /**
* @phpstan-param Record $record
*/
private function handleExceptionRecord(array $record): void
{ {
$this->connector->getErrorsDispatcher()->dispatchException($record->context['exception']); $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
} }
private function handleErrorRecord(LogRecord $record): void /**
* @phpstan-param Record $record
*/
private function handleErrorRecord(array $record): void
{ {
$context = $record->context; $context = $record['context'];
$this->connector->getErrorsDispatcher()->dispatchError( $this->connector->getErrorsDispatcher()->dispatchError(
$context['code'] ?? null, $context['code'] ?? null,
$context['message'] ?? $record->message, $context['message'] ?? $record['message'],
$context['file'] ?? null, $context['file'] ?? null,
$context['line'] ?? null, $context['line'] ?? null,
$this->options['classesPartialsTraceIgnore'] $this->options['classesPartialsTraceIgnore']
@ -269,32 +229,32 @@ class PHPConsoleHandler extends AbstractProcessingHandler
} }
/** /**
* @return array{string, mixed[]} * @phpstan-param Record $record
* @return string
*/ */
private function getRecordTags(LogRecord $record): array private function getRecordTags(array &$record)
{ {
$tags = null; $tags = null;
$filteredContext = []; if (!empty($record['context'])) {
if ($record->context !== []) { $context = & $record['context'];
$filteredContext = $record->context;
foreach ($this->options['debugTagsKeysInContext'] as $key) { foreach ($this->options['debugTagsKeysInContext'] as $key) {
if (isset($filteredContext[$key])) { if (!empty($context[$key])) {
$tags = $filteredContext[$key]; $tags = $context[$key];
if ($key === 0) { if ($key === 0) {
array_shift($filteredContext); array_shift($context);
} else { } else {
unset($filteredContext[$key]); unset($context[$key]);
} }
break; break;
} }
} }
} }
return [$tags ?? $record->level->toPsrLogLevel(), $filteredContext]; return $tags ?: strtolower($record['level_name']);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,8 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Stores to STDIN of any process, specified by a command. * Stores to STDIN of any process, specified by a command.
@ -34,14 +33,20 @@ class ProcessHandler extends AbstractProcessingHandler
*/ */
private $process; private $process;
private string $command; /**
* @var string
*/
private $command;
private ?string $cwd; /**
* @var string|null
*/
private $cwd;
/** /**
* @var resource[] * @var resource[]
*/ */
private array $pipes = []; private $pipes = [];
/** /**
* @var array<int, string[]> * @var array<int, string[]>
@ -58,7 +63,7 @@ class ProcessHandler extends AbstractProcessingHandler
* @param string|null $cwd "Current working directory" (CWD) for the process to be executed in. * @param string|null $cwd "Current working directory" (CWD) for the process to be executed in.
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function __construct(string $command, int|string|Level $level = Level::Debug, bool $bubble = true, ?string $cwd = null) public function __construct(string $command, $level = Logger::DEBUG, bool $bubble = true, ?string $cwd = null)
{ {
if ($command === '') { if ($command === '') {
throw new \InvalidArgumentException('The command argument must be a non-empty string.'); throw new \InvalidArgumentException('The command argument must be a non-empty string.');
@ -78,14 +83,14 @@ class ProcessHandler extends AbstractProcessingHandler
* *
* @throws \UnexpectedValueException * @throws \UnexpectedValueException
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->ensureProcessIsStarted(); $this->ensureProcessIsStarted();
$this->writeProcessInput($record->formatted); $this->writeProcessInput($record['formatted']);
$errors = $this->readProcessErrors(); $errors = $this->readProcessErrors();
if ($errors !== '') { if (empty($errors) === false) {
throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors)); throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors));
} }
} }
@ -129,7 +134,7 @@ class ProcessHandler extends AbstractProcessingHandler
$errors = $this->readProcessErrors(); $errors = $this->readProcessErrors();
if (is_resource($this->process) === false || $errors !== '') { if (is_resource($this->process) === false || empty($errors) === false) {
throw new \UnexpectedValueException( throw new \UnexpectedValueException(
sprintf('The process "%s" could not be opened: ' . $errors, $this->command) sprintf('The process "%s" could not be opened: ' . $errors, $this->command)
); );
@ -171,7 +176,7 @@ class ProcessHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {

View file

@ -12,19 +12,20 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Processor\ProcessorInterface; use Monolog\Processor\ProcessorInterface;
use Monolog\LogRecord;
/** /**
* Interface to describe loggers that have processors * Interface to describe loggers that have processors
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
interface ProcessableHandlerInterface interface ProcessableHandlerInterface
{ {
/** /**
* Adds a processor in the stack. * Adds a processor in the stack.
* *
* @phpstan-param ProcessorInterface|(callable(LogRecord): LogRecord) $callback * @psalm-param ProcessorInterface|callable(Record): Record $callback
* *
* @param ProcessorInterface|callable $callback * @param ProcessorInterface|callable $callback
* @return HandlerInterface self * @return HandlerInterface self
@ -34,7 +35,7 @@ interface ProcessableHandlerInterface
/** /**
* Removes the processor on top of the stack and returns it. * Removes the processor on top of the stack and returns it.
* *
* @phpstan-return ProcessorInterface|(callable(LogRecord): LogRecord) $callback * @psalm-return ProcessorInterface|callable(Record): Record $callback
* *
* @throws \LogicException In case the processor stack is empty * @throws \LogicException In case the processor stack is empty
* @return callable|ProcessorInterface * @return callable|ProcessorInterface

View file

@ -13,23 +13,24 @@ namespace Monolog\Handler;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
use Monolog\Processor\ProcessorInterface; use Monolog\Processor\ProcessorInterface;
use Monolog\LogRecord;
/** /**
* Helper trait for implementing ProcessableInterface * Helper trait for implementing ProcessableInterface
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
trait ProcessableHandlerTrait trait ProcessableHandlerTrait
{ {
/** /**
* @var callable[] * @var callable[]
* @phpstan-var array<(callable(LogRecord): LogRecord)|ProcessorInterface> * @phpstan-var array<ProcessorInterface|callable(Record): Record>
*/ */
protected array $processors = []; protected $processors = [];
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function pushProcessor(callable $callback): HandlerInterface public function pushProcessor(callable $callback): HandlerInterface
{ {
@ -39,18 +40,24 @@ trait ProcessableHandlerTrait
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function popProcessor(): callable public function popProcessor(): callable
{ {
if (\count($this->processors) === 0) { if (!$this->processors) {
throw new \LogicException('You tried to pop from an empty processor stack.'); throw new \LogicException('You tried to pop from an empty processor stack.');
} }
return array_shift($this->processors); return array_shift($this->processors);
} }
protected function processRecord(LogRecord $record): LogRecord /**
* Processes a record.
*
* @phpstan-param Record $record
* @phpstan-return Record
*/
protected function processRecord(array $record): array
{ {
foreach ($this->processors as $processor) { foreach ($this->processors as $processor) {
$record = $processor($record); $record = $processor($record);

View file

@ -11,10 +11,9 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Proxies log messages to an existing PSR-3 compliant logger. * Proxies log messages to an existing PSR-3 compliant logger.
@ -29,15 +28,20 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
{ {
/** /**
* PSR-3 compliant logger * PSR-3 compliant logger
*
* @var LoggerInterface
*/ */
protected LoggerInterface $logger; protected $logger;
protected FormatterInterface|null $formatter = null; /**
* @var FormatterInterface|null
*/
protected $formatter;
/** /**
* @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied
*/ */
public function __construct(LoggerInterface $logger, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -45,19 +49,19 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (!$this->isHandling($record)) { if (!$this->isHandling($record)) {
return false; return false;
} }
if ($this->formatter !== null) { if ($this->formatter) {
$formatted = $this->formatter->format($record); $formatted = $this->formatter->format($record);
$this->logger->log($record->level->toPsrLogLevel(), (string) $formatted, $record->context); $this->logger->log(strtolower($record['level_name']), (string) $formatted, $record['context']);
} else { } else {
$this->logger->log($record->level->toPsrLogLevel(), $record->message, $record->context); $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']);
} }
return false === $this->bubble; return false === $this->bubble;
@ -65,6 +69,8 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
/** /**
* Sets the formatter. * Sets the formatter.
*
* @param FormatterInterface $formatter
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -75,10 +81,12 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
/** /**
* Gets the formatter. * Gets the formatter.
*
* @return FormatterInterface
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {
if ($this->formatter === null) { if (!$this->formatter) {
throw new \LogicException('No formatter has been set and this handler does not have a default formatter'); throw new \LogicException('No formatter has been set and this handler does not have a default formatter');
} }

View file

@ -11,45 +11,48 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Sends notifications through the pushover api to mobile phones * Sends notifications through the pushover api to mobile phones
* *
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com> * @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
* @see https://www.pushover.net/api * @see https://www.pushover.net/api
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class PushoverHandler extends SocketHandler class PushoverHandler extends SocketHandler
{ {
private string $token; /** @var string */
private $token;
/** @var array<int|string> */ /** @var array<int|string> */
private array $users; private $users;
/** @var string */
private $title;
/** @var string|int|null */
private $user = null;
/** @var int */
private $retry;
/** @var int */
private $expire;
private string $title; /** @var int */
private $highPriorityLevel;
private string|int|null $user = null; /** @var int */
private $emergencyLevel;
private int $retry; /** @var bool */
private $useFormattedMessage = false;
private int $expire;
private Level $highPriorityLevel;
private Level $emergencyLevel;
private bool $useFormattedMessage = false;
/** /**
* All parameters that can be sent to Pushover * All parameters that can be sent to Pushover
* @see https://pushover.net/api * @see https://pushover.net/api
* @var array<string, bool> * @var array<string, bool>
*/ */
private array $parameterNames = [ private $parameterNames = [
'token' => true, 'token' => true,
'user' => true, 'user' => true,
'message' => true, 'message' => true,
@ -70,7 +73,7 @@ class PushoverHandler extends SocketHandler
* @see https://pushover.net/api#sounds * @see https://pushover.net/api#sounds
* @var string[] * @var string[]
*/ */
private array $sounds = [ private $sounds = [
'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb',
'persistent', 'echo', 'updown', 'none', 'persistent', 'echo', 'updown', 'none',
@ -82,30 +85,28 @@ class PushoverHandler extends SocketHandler
* @param string|null $title Title sent to the Pushover API * @param string|null $title Title sent to the Pushover API
* @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
* the pushover.net app owner. OpenSSL is required for this option. * the pushover.net app owner. OpenSSL is required for this option.
* @param string|int $highPriorityLevel The minimum logging level at which this handler will start
* sending "high priority" requests to the Pushover API
* @param string|int $emergencyLevel The minimum logging level at which this handler will start
* sending "emergency" requests to the Pushover API
* @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will
* send the same notification to the user. * send the same notification to the user.
* @param int $expire The expire parameter specifies how many seconds your notification will continue * @param int $expire The expire parameter specifies how many seconds your notification will continue
* to be retried for (every retry seconds). * to be retried for (every retry seconds).
* *
* @param int|string|Level|LogLevel::* $highPriorityLevel The minimum logging level at which this handler will start
* sending "high priority" requests to the Pushover API
* @param int|string|Level|LogLevel::* $emergencyLevel The minimum logging level at which this handler will start
* sending "emergency" requests to the Pushover API
*
*
* @phpstan-param string|array<int|string> $users * @phpstan-param string|array<int|string> $users
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $highPriorityLevel * @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $emergencyLevel * @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel
*/ */
public function __construct( public function __construct(
string $token, string $token,
$users, $users,
?string $title = null, ?string $title = null,
int|string|Level $level = Level::Critical, $level = Logger::CRITICAL,
bool $bubble = true, bool $bubble = true,
bool $useSSL = true, bool $useSSL = true,
int|string|Level $highPriorityLevel = Level::Critical, $highPriorityLevel = Logger::CRITICAL,
int|string|Level $emergencyLevel = Level::Emergency, $emergencyLevel = Logger::EMERGENCY,
int $retry = 30, int $retry = 30,
int $expire = 25200, int $expire = 25200,
bool $persistent = false, bool $persistent = false,
@ -128,29 +129,32 @@ class PushoverHandler extends SocketHandler
$this->token = $token; $this->token = $token;
$this->users = (array) $users; $this->users = (array) $users;
$this->title = $title ?? (string) gethostname(); $this->title = $title ?: (string) gethostname();
$this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
$this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
$this->retry = $retry; $this->retry = $retry;
$this->expire = $expire; $this->expire = $expire;
} }
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
$content = $this->buildContent($record); $content = $this->buildContent($record);
return $this->buildHeader($content) . $content; return $this->buildHeader($content) . $content;
} }
private function buildContent(LogRecord $record): string /**
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{ {
// Pushover has a limit of 512 characters on title and message combined. // Pushover has a limit of 512 characters on title and message combined.
$maxMessageLength = 512 - strlen($this->title); $maxMessageLength = 512 - strlen($this->title);
$message = ($this->useFormattedMessage) ? $record->formatted : $record->message; $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message'];
$message = Utils::substr($message, 0, $maxMessageLength); $message = Utils::substr($message, 0, $maxMessageLength);
$timestamp = $record->datetime->getTimestamp(); $timestamp = $record['datetime']->getTimestamp();
$dataArray = [ $dataArray = [
'token' => $this->token, 'token' => $this->token,
@ -160,23 +164,23 @@ class PushoverHandler extends SocketHandler
'timestamp' => $timestamp, 'timestamp' => $timestamp,
]; ];
if ($record->level->value >= $this->emergencyLevel->value) { if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) {
$dataArray['priority'] = 2; $dataArray['priority'] = 2;
$dataArray['retry'] = $this->retry; $dataArray['retry'] = $this->retry;
$dataArray['expire'] = $this->expire; $dataArray['expire'] = $this->expire;
} elseif ($record->level->value >= $this->highPriorityLevel->value) { } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) {
$dataArray['priority'] = 1; $dataArray['priority'] = 1;
} }
// First determine the available parameters // First determine the available parameters
$context = array_intersect_key($record->context, $this->parameterNames); $context = array_intersect_key($record['context'], $this->parameterNames);
$extra = array_intersect_key($record->extra, $this->parameterNames); $extra = array_intersect_key($record['extra'], $this->parameterNames);
// Least important info should be merged with subsequent info // Least important info should be merged with subsequent info
$dataArray = array_merge($extra, $context, $dataArray); $dataArray = array_merge($extra, $context, $dataArray);
// Only pass sounds that are supported by the API // Only pass sounds that are supported by the API
if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds, true)) { if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) {
unset($dataArray['sound']); unset($dataArray['sound']);
} }
@ -194,7 +198,7 @@ class PushoverHandler extends SocketHandler
return $header; return $header;
} }
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
foreach ($this->users as $user) { foreach ($this->users as $user) {
$this->user = $user; $this->user = $user;
@ -207,25 +211,25 @@ class PushoverHandler extends SocketHandler
} }
/** /**
* @param int|string|Level|LogLevel::* $level * @param int|string $value
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level * @phpstan-param Level|LevelName|LogLevel::* $value
*/ */
public function setHighPriorityLevel(int|string|Level $level): self public function setHighPriorityLevel($value): self
{ {
$this->highPriorityLevel = Logger::toMonologLevel($level); $this->highPriorityLevel = Logger::toMonologLevel($value);
return $this; return $this;
} }
/** /**
* @param int|string|Level|LogLevel::* $level * @param int|string $value
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level * @phpstan-param Level|LevelName|LogLevel::* $value
*/ */
public function setEmergencyLevel(int|string|Level $level): self public function setEmergencyLevel($value): self
{ {
$this->emergencyLevel = Logger::toMonologLevel($level); $this->emergencyLevel = Logger::toMonologLevel($value);
return $this; return $this;
} }
@ -233,9 +237,9 @@ class PushoverHandler extends SocketHandler
/** /**
* Use the formatted message? * Use the formatted message?
*/ */
public function useFormattedMessage(bool $useFormattedMessage): self public function useFormattedMessage(bool $value): self
{ {
$this->useFormattedMessage = $useFormattedMessage; $this->useFormattedMessage = $value;
return $this; return $this;
} }

View file

@ -13,10 +13,7 @@ namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
use Predis\Client as Predis;
use Redis;
/** /**
* Logs to a Redis key using rpush * Logs to a Redis key using rpush
@ -28,21 +25,29 @@ use Redis;
* $log->pushHandler($redis); * $log->pushHandler($redis);
* *
* @author Thomas Tourlourat <thomas@tourlourat.com> * @author Thomas Tourlourat <thomas@tourlourat.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class RedisHandler extends AbstractProcessingHandler class RedisHandler extends AbstractProcessingHandler
{ {
/** @var Predis<Predis>|Redis */ /** @var \Predis\Client<\Predis\Client>|\Redis */
private Predis|Redis $redisClient; private $redisClient;
private string $redisKey; /** @var string */
protected int $capSize; private $redisKey;
/** @var int */
protected $capSize;
/** /**
* @param Predis<Predis>|Redis $redis The redis instance * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance
* @param string $key The key name to push records to * @param string $key The key name to push records to
* @param int $capSize Number of entries to limit list size to, 0 = unlimited * @param int $capSize Number of entries to limit list size to, 0 = unlimited
*/ */
public function __construct(Predis|Redis $redis, string $key, int|string|Level $level = Level::Debug, bool $bubble = true, int $capSize = 0) public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true, int $capSize = 0)
{ {
if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) {
throw new \InvalidArgumentException('Predis\Client or Redis instance required');
}
$this->redisClient = $redis; $this->redisClient = $redis;
$this->redisKey = $key; $this->redisKey = $key;
$this->capSize = $capSize; $this->capSize = $capSize;
@ -51,41 +56,43 @@ class RedisHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if ($this->capSize > 0) { if ($this->capSize) {
$this->writeCapped($record); $this->writeCapped($record);
} else { } else {
$this->redisClient->rpush($this->redisKey, $record->formatted); $this->redisClient->rpush($this->redisKey, $record["formatted"]);
} }
} }
/** /**
* Write and cap the collection * Write and cap the collection
* Writes the record to the redis list and caps its * Writes the record to the redis list and caps its
*
* @phpstan-param FormattedRecord $record
*/ */
protected function writeCapped(LogRecord $record): void protected function writeCapped(array $record): void
{ {
if ($this->redisClient instanceof Redis) { if ($this->redisClient instanceof \Redis) {
$mode = defined('Redis::MULTI') ? Redis::MULTI : 1; $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1;
$this->redisClient->multi($mode) $this->redisClient->multi($mode)
->rPush($this->redisKey, $record->formatted) ->rpush($this->redisKey, $record["formatted"])
->ltrim($this->redisKey, -$this->capSize, -1) ->ltrim($this->redisKey, -$this->capSize, -1)
->exec(); ->exec();
} else { } else {
$redisKey = $this->redisKey; $redisKey = $this->redisKey;
$capSize = $this->capSize; $capSize = $this->capSize;
$this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) {
$tx->rpush($redisKey, $record->formatted); $tx->rpush($redisKey, $record["formatted"]);
$tx->ltrim($redisKey, -$capSize, -1); $tx->ltrim($redisKey, -$capSize, -1);
}); });
} }
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -13,10 +13,7 @@ namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
use Predis\Client as Predis;
use Redis;
/** /**
* Sends the message to a Redis Pub/Sub channel using PUBLISH * Sends the message to a Redis Pub/Sub channel using PUBLISH
@ -24,23 +21,28 @@ use Redis;
* usage example: * usage example:
* *
* $log = new Logger('application'); * $log = new Logger('application');
* $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Level::Warning); * $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING);
* $log->pushHandler($redis); * $log->pushHandler($redis);
* *
* @author Gaëtan Faugère <gaetan@fauge.re> * @author Gaëtan Faugère <gaetan@fauge.re>
*/ */
class RedisPubSubHandler extends AbstractProcessingHandler class RedisPubSubHandler extends AbstractProcessingHandler
{ {
/** @var Predis<Predis>|Redis */ /** @var \Predis\Client<\Predis\Client>|\Redis */
private Predis|Redis $redisClient; private $redisClient;
private string $channelKey; /** @var string */
private $channelKey;
/** /**
* @param Predis<Predis>|Redis $redis The redis instance * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance
* @param string $key The channel key to publish records to * @param string $key The channel key to publish records to
*/ */
public function __construct(Predis|Redis $redis, string $key, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true)
{ {
if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) {
throw new \InvalidArgumentException('Predis\Client or Redis instance required');
}
$this->redisClient = $redis; $this->redisClient = $redis;
$this->channelKey = $key; $this->channelKey = $key;
@ -48,15 +50,15 @@ class RedisPubSubHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->redisClient->publish($this->channelKey, $record->formatted); $this->redisClient->publish($this->channelKey, $record["formatted"]);
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter(): FormatterInterface
{ {

View file

@ -11,10 +11,9 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level;
use Rollbar\RollbarLogger; use Rollbar\RollbarLogger;
use Throwable; use Throwable;
use Monolog\LogRecord; use Monolog\Logger;
/** /**
* Sends errors to Rollbar * Sends errors to Rollbar
@ -34,19 +33,37 @@ use Monolog\LogRecord;
*/ */
class RollbarHandler extends AbstractProcessingHandler class RollbarHandler extends AbstractProcessingHandler
{ {
protected RollbarLogger $rollbarLogger; /**
* @var RollbarLogger
*/
protected $rollbarLogger;
/** @var string[] */
protected $levelMap = [
Logger::DEBUG => 'debug',
Logger::INFO => 'info',
Logger::NOTICE => 'info',
Logger::WARNING => 'warning',
Logger::ERROR => 'error',
Logger::CRITICAL => 'critical',
Logger::ALERT => 'critical',
Logger::EMERGENCY => 'critical',
];
/** /**
* Records whether any log records have been added since the last flush of the rollbar notifier * Records whether any log records have been added since the last flush of the rollbar notifier
*
* @var bool
*/ */
private bool $hasRecords = false; private $hasRecords = false;
protected bool $initialized = false; /** @var bool */
protected $initialized = false;
/** /**
* @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token
*/ */
public function __construct(RollbarLogger $rollbarLogger, int|string|Level $level = Level::Error, bool $bubble = true) public function __construct(RollbarLogger $rollbarLogger, $level = Logger::ERROR, bool $bubble = true)
{ {
$this->rollbarLogger = $rollbarLogger; $this->rollbarLogger = $rollbarLogger;
@ -54,41 +71,22 @@ class RollbarHandler extends AbstractProcessingHandler
} }
/** /**
* Translates Monolog log levels to Rollbar levels. * {@inheritDoc}
*
* @return 'debug'|'info'|'warning'|'error'|'critical'
*/ */
protected function toRollbarLevel(Level $level): string protected function write(array $record): void
{
return match ($level) {
Level::Debug => 'debug',
Level::Info => 'info',
Level::Notice => 'info',
Level::Warning => 'warning',
Level::Error => 'error',
Level::Critical => 'critical',
Level::Alert => 'critical',
Level::Emergency => 'critical',
};
}
/**
* @inheritDoc
*/
protected function write(LogRecord $record): void
{ {
if (!$this->initialized) { if (!$this->initialized) {
// __destructor() doesn't get called on Fatal errors // __destructor() doesn't get called on Fatal errors
register_shutdown_function([$this, 'close']); register_shutdown_function(array($this, 'close'));
$this->initialized = true; $this->initialized = true;
} }
$context = $record->context; $context = $record['context'];
$context = array_merge($context, $record->extra, [ $context = array_merge($context, $record['extra'], [
'level' => $this->toRollbarLevel($record->level), 'level' => $this->levelMap[$record['level']],
'monolog_level' => $record->level->getName(), 'monolog_level' => $record['level_name'],
'channel' => $record->channel, 'channel' => $record['channel'],
'datetime' => $record->datetime->format('U'), 'datetime' => $record['datetime']->format('U'),
]); ]);
if (isset($context['exception']) && $context['exception'] instanceof Throwable) { if (isset($context['exception']) && $context['exception'] instanceof Throwable) {
@ -96,7 +94,7 @@ class RollbarHandler extends AbstractProcessingHandler
unset($context['exception']); unset($context['exception']);
$toLog = $exception; $toLog = $exception;
} else { } else {
$toLog = $record->message; $toLog = $record['message'];
} }
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
@ -114,7 +112,7 @@ class RollbarHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
@ -122,9 +120,9 @@ class RollbarHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function reset(): void public function reset()
{ {
$this->flush(); $this->flush();

View file

@ -12,9 +12,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use InvalidArgumentException; use InvalidArgumentException;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Stores logs to files that are rotated every day and a limited number of files are kept. * Stores logs to files that are rotated every day and a limited number of files are kept.
@ -31,19 +30,26 @@ class RotatingFileHandler extends StreamHandler
public const FILE_PER_MONTH = 'Y-m'; public const FILE_PER_MONTH = 'Y-m';
public const FILE_PER_YEAR = 'Y'; public const FILE_PER_YEAR = 'Y';
protected string $filename; /** @var string */
protected int $maxFiles; protected $filename;
protected bool|null $mustRotate = null; /** @var int */
protected \DateTimeImmutable $nextRotation; protected $maxFiles;
protected string $filenameFormat; /** @var bool */
protected string $dateFormat; protected $mustRotate;
/** @var \DateTimeImmutable */
protected $nextRotation;
/** @var string */
protected $filenameFormat;
/** @var string */
protected $dateFormat;
/** /**
* @param string $filename
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited) * @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
* @param bool $useLocking Try to lock log file before doing any writes * @param bool $useLocking Try to lock log file before doing any writes
*/ */
public function __construct(string $filename, int $maxFiles = 0, int|string|Level $level = Level::Debug, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
{ {
$this->filename = Utils::canonicalizePath($filename); $this->filename = Utils::canonicalizePath($filename);
$this->maxFiles = $maxFiles; $this->maxFiles = $maxFiles;
@ -55,7 +61,7 @@ class RotatingFileHandler extends StreamHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
@ -67,9 +73,9 @@ class RotatingFileHandler extends StreamHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function reset(): void public function reset()
{ {
parent::reset(); parent::reset();
@ -80,7 +86,7 @@ class RotatingFileHandler extends StreamHandler
public function setFilenameFormat(string $filenameFormat, string $dateFormat): self public function setFilenameFormat(string $filenameFormat, string $dateFormat): self
{ {
if (0 === preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { if (!preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
'Invalid date format - format must be one of '. 'Invalid date format - format must be one of '.
'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '.
@ -102,16 +108,16 @@ class RotatingFileHandler extends StreamHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
// on the first record written, if the log is new, we should rotate (once per day) // on the first record written, if the log is new, we should rotate (once per day)
if (null === $this->mustRotate) { if (null === $this->mustRotate) {
$this->mustRotate = null === $this->url || !file_exists($this->url); $this->mustRotate = null === $this->url || !file_exists($this->url);
} }
if ($this->nextRotation <= $record->datetime) { if ($this->nextRotation <= $record['datetime']) {
$this->mustRotate = true; $this->mustRotate = true;
$this->close(); $this->close();
} }

View file

@ -11,9 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Closure;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Sampling handler * Sampling handler
@ -28,42 +26,52 @@ use Monolog\LogRecord;
* *
* @author Bryan Davis <bd808@wikimedia.org> * @author Bryan Davis <bd808@wikimedia.org>
* @author Kunal Mehta <legoktm@gmail.com> * @author Kunal Mehta <legoktm@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/ */
class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{ {
use ProcessableHandlerTrait; use ProcessableHandlerTrait;
/** /**
* Handler or factory Closure($record, $this) * @var HandlerInterface|callable
* * @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface
* @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
*/ */
protected Closure|HandlerInterface $handler; protected $handler;
protected int $factor;
/** /**
* @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler * @var int $factor
*/
protected $factor;
/**
* @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler
* *
* @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $samplingHandler). * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler).
* @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled)
*/ */
public function __construct(Closure|HandlerInterface $handler, int $factor) public function __construct($handler, int $factor)
{ {
parent::__construct(); parent::__construct();
$this->handler = $handler; $this->handler = $handler;
$this->factor = $factor; $this->factor = $factor;
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
}
} }
public function isHandling(LogRecord $record): bool public function isHandling(array $record): bool
{ {
return $this->getHandler($record)->isHandling($record); return $this->getHandler($record)->isHandling($record);
} }
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
@ -76,23 +84,26 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
/** /**
* Return the nested handler * Return the nested handler
* *
* If the handler was provided as a factory, this will trigger the handler's instantiation. * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @phpstan-param Record|array{level: Level}|null $record
*
* @return HandlerInterface
*/ */
public function getHandler(LogRecord $record = null): HandlerInterface public function getHandler(array $record = null)
{ {
if (!$this->handler instanceof HandlerInterface) { if (!$this->handler instanceof HandlerInterface) {
$handler = ($this->handler)($record, $this); $this->handler = ($this->handler)($record, $this);
if (!$handler instanceof HandlerInterface) { if (!$this->handler instanceof HandlerInterface) {
throw new \RuntimeException("The factory Closure should return a HandlerInterface"); throw new \RuntimeException("The factory callable should return a HandlerInterface");
} }
$this->handler = $handler;
} }
return $this->handler; return $this->handler;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter): HandlerInterface
{ {
@ -107,7 +118,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter(): FormatterInterface
{ {

View file

@ -11,7 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
/** /**
* SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html * SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html
@ -22,29 +22,33 @@ class SendGridHandler extends MailHandler
{ {
/** /**
* The SendGrid API User * The SendGrid API User
* @var string
*/ */
protected string $apiUser; protected $apiUser;
/** /**
* The SendGrid API Key * The SendGrid API Key
* @var string
*/ */
protected string $apiKey; protected $apiKey;
/** /**
* The email addresses to which the message will be sent * The email addresses to which the message will be sent
* @var string
*/ */
protected string $from; protected $from;
/** /**
* The email addresses to which the message will be sent * The email addresses to which the message will be sent
* @var string[] * @var string[]
*/ */
protected array $to; protected $to;
/** /**
* The subject of the email * The subject of the email
* @var string
*/ */
protected string $subject; protected $subject;
/** /**
* @param string $apiUser The SendGrid API User * @param string $apiUser The SendGrid API User
@ -53,7 +57,7 @@ class SendGridHandler extends MailHandler
* @param string|string[] $to The recipients of the email * @param string|string[] $to The recipients of the email
* @param string $subject The subject of the mail * @param string $subject The subject of the mail
*/ */
public function __construct(string $apiUser, string $apiKey, string $from, string|array $to, string $subject, int|string|Level $level = Level::Error, bool $bubble = true) public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true)
{ {
if (!extension_loaded('curl')) { if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler'); throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler');
@ -68,7 +72,7 @@ class SendGridHandler extends MailHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function send(string $content, array $records): void protected function send(string $content, array $records): void
{ {

View file

@ -11,11 +11,10 @@
namespace Monolog\Handler\Slack; namespace Monolog\Handler\Slack;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\Formatter\NormalizerFormatter; use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;
/** /**
* Slack record utility helping to log to Slack webhooks or API. * Slack record utility helping to log to Slack webhooks or API.
@ -24,6 +23,9 @@ use Monolog\LogRecord;
* @author Haralan Dobrev <hkdobrev@gmail.com> * @author Haralan Dobrev <hkdobrev@gmail.com>
* @see https://api.slack.com/incoming-webhooks * @see https://api.slack.com/incoming-webhooks
* @see https://api.slack.com/docs/message-attachments * @see https://api.slack.com/docs/message-attachments
*
* @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class SlackRecord class SlackRecord
{ {
@ -37,43 +39,55 @@ class SlackRecord
/** /**
* Slack channel (encoded ID or name) * Slack channel (encoded ID or name)
* @var string|null
*/ */
private string|null $channel; private $channel;
/** /**
* Name of a bot * Name of a bot
* @var string|null
*/ */
private string|null $username; private $username;
/** /**
* User icon e.g. 'ghost', 'http://example.com/user.png' * User icon e.g. 'ghost', 'http://example.com/user.png'
* @var string|null
*/ */
private string|null $userIcon; private $userIcon;
/** /**
* Whether the message should be added to Slack as attachment (plain text otherwise) * Whether the message should be added to Slack as attachment (plain text otherwise)
* @var bool
*/ */
private bool $useAttachment; private $useAttachment;
/** /**
* Whether the the context/extra messages added to Slack as attachments are in a short style * Whether the the context/extra messages added to Slack as attachments are in a short style
* @var bool
*/ */
private bool $useShortAttachment; private $useShortAttachment;
/** /**
* Whether the attachment should include context and extra data * Whether the attachment should include context and extra data
* @var bool
*/ */
private bool $includeContextAndExtra; private $includeContextAndExtra;
/** /**
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
* @var string[] * @var string[]
*/ */
private array $excludeFields; private $excludeFields;
private FormatterInterface|null $formatter; /**
* @var ?FormatterInterface
*/
private $formatter;
private NormalizerFormatter $normalizerFormatter; /**
* @var NormalizerFormatter
*/
private $normalizerFormatter;
/** /**
* @param string[] $excludeFields * @param string[] $excludeFields
@ -85,7 +99,7 @@ class SlackRecord
?string $userIcon = null, ?string $userIcon = null,
bool $useShortAttachment = false, bool $useShortAttachment = false,
bool $includeContextAndExtra = false, bool $includeContextAndExtra = false,
array $excludeFields = [], array $excludeFields = array(),
FormatterInterface $formatter = null FormatterInterface $formatter = null
) { ) {
$this $this
@ -107,76 +121,77 @@ class SlackRecord
* Returns required data in format that Slack * Returns required data in format that Slack
* is expecting. * is expecting.
* *
* @phpstan-param FormattedRecord $record
* @phpstan-return mixed[] * @phpstan-return mixed[]
*/ */
public function getSlackData(LogRecord $record): array public function getSlackData(array $record): array
{ {
$dataArray = []; $dataArray = array();
$record = $this->removeExcludedFields($record);
if ($this->username !== null) { if ($this->username) {
$dataArray['username'] = $this->username; $dataArray['username'] = $this->username;
} }
if ($this->channel !== null) { if ($this->channel) {
$dataArray['channel'] = $this->channel; $dataArray['channel'] = $this->channel;
} }
if ($this->formatter !== null && !$this->useAttachment) { if ($this->formatter && !$this->useAttachment) {
/** @phpstan-ignore-next-line */
$message = $this->formatter->format($record); $message = $this->formatter->format($record);
} else { } else {
$message = $record->message; $message = $record['message'];
} }
$recordData = $this->removeExcludedFields($record);
if ($this->useAttachment) { if ($this->useAttachment) {
$attachment = [ $attachment = array(
'fallback' => $message, 'fallback' => $message,
'text' => $message, 'text' => $message,
'color' => $this->getAttachmentColor($record->level), 'color' => $this->getAttachmentColor($record['level']),
'fields' => [], 'fields' => array(),
'mrkdwn_in' => ['fields'], 'mrkdwn_in' => array('fields'),
'ts' => $recordData['datetime']->getTimestamp(), 'ts' => $record['datetime']->getTimestamp(),
'footer' => $this->username, 'footer' => $this->username,
'footer_icon' => $this->userIcon, 'footer_icon' => $this->userIcon,
]; );
if ($this->useShortAttachment) { if ($this->useShortAttachment) {
$attachment['title'] = $recordData['level_name']; $attachment['title'] = $record['level_name'];
} else { } else {
$attachment['title'] = 'Message'; $attachment['title'] = 'Message';
$attachment['fields'][] = $this->generateAttachmentField('Level', $recordData['level_name']); $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']);
} }
if ($this->includeContextAndExtra) { if ($this->includeContextAndExtra) {
foreach (['extra', 'context'] as $key) { foreach (array('extra', 'context') as $key) {
if (!isset($recordData[$key]) || \count($recordData[$key]) === 0) { if (empty($record[$key])) {
continue; continue;
} }
if ($this->useShortAttachment) { if ($this->useShortAttachment) {
$attachment['fields'][] = $this->generateAttachmentField( $attachment['fields'][] = $this->generateAttachmentField(
$key, (string) $key,
$recordData[$key] $record[$key]
); );
} else { } else {
// Add all extra fields as individual fields in attachment // Add all extra fields as individual fields in attachment
$attachment['fields'] = array_merge( $attachment['fields'] = array_merge(
$attachment['fields'], $attachment['fields'],
$this->generateAttachmentFields($recordData[$key]) $this->generateAttachmentFields($record[$key])
); );
} }
} }
} }
$dataArray['attachments'] = [$attachment]; $dataArray['attachments'] = array($attachment);
} else { } else {
$dataArray['text'] = $message; $dataArray['text'] = $message;
} }
if ($this->userIcon !== null) { if ($this->userIcon) {
if (false !== ($iconUrl = filter_var($this->userIcon, FILTER_VALIDATE_URL))) { if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
$dataArray['icon_url'] = $iconUrl; $dataArray['icon_url'] = $this->userIcon;
} else { } else {
$dataArray['icon_emoji'] = ":{$this->userIcon}:"; $dataArray['icon_emoji'] = ":{$this->userIcon}:";
} }
@ -189,14 +204,18 @@ class SlackRecord
* Returns a Slack message attachment color associated with * Returns a Slack message attachment color associated with
* provided level. * provided level.
*/ */
public function getAttachmentColor(Level $level): string public function getAttachmentColor(int $level): string
{ {
return match ($level) { switch (true) {
Level::Error, Level::Critical, Level::Alert, Level::Emergency => static::COLOR_DANGER, case $level >= Logger::ERROR:
Level::Warning => static::COLOR_WARNING, return static::COLOR_DANGER;
Level::Info, Level::Notice => static::COLOR_GOOD, case $level >= Logger::WARNING:
Level::Debug => static::COLOR_DEFAULT return static::COLOR_WARNING;
}; case $level >= Logger::INFO:
return static::COLOR_GOOD;
default:
return static::COLOR_DEFAULT;
}
} }
/** /**
@ -206,13 +225,13 @@ class SlackRecord
*/ */
public function stringify(array $fields): string public function stringify(array $fields): string
{ {
/** @var array<mixed> $normalized */ /** @var Record $fields */
$normalized = $this->normalizerFormatter->normalizeValue($fields); $normalized = $this->normalizerFormatter->format($fields);
$hasSecondDimension = \count(array_filter($normalized, 'is_array')) > 0; $hasSecondDimension = count(array_filter($normalized, 'is_array'));
$hasOnlyNonNumericKeys = \count(array_filter(array_keys($normalized), 'is_numeric')) === 0; $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
return $hasSecondDimension || $hasOnlyNonNumericKeys return $hasSecondDimension || $hasNonNumericKeys
? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS) ? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS)
: Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS); : Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS);
} }
@ -311,11 +330,11 @@ class SlackRecord
? sprintf('```%s```', substr($this->stringify($value), 0, 1990)) ? sprintf('```%s```', substr($this->stringify($value), 0, 1990))
: $value; : $value;
return [ return array(
'title' => ucfirst($title), 'title' => ucfirst($title),
'value' => $value, 'value' => $value,
'short' => false, 'short' => false,
]; );
} }
/** /**
@ -327,10 +346,10 @@ class SlackRecord
*/ */
private function generateAttachmentFields(array $data): array private function generateAttachmentFields(array $data): array
{ {
/** @var array<mixed> $normalized */ /** @var Record $data */
$normalized = $this->normalizerFormatter->normalizeValue($data); $normalized = $this->normalizerFormatter->format($data);
$fields = []; $fields = array();
foreach ($normalized as $key => $value) { foreach ($normalized as $key => $value) {
$fields[] = $this->generateAttachmentField((string) $key, $value); $fields[] = $this->generateAttachmentField((string) $key, $value);
} }
@ -341,14 +360,15 @@ class SlackRecord
/** /**
* Get a copy of record with fields excluded according to $this->excludeFields * Get a copy of record with fields excluded according to $this->excludeFields
* *
* @phpstan-param FormattedRecord $record
*
* @return mixed[] * @return mixed[]
*/ */
private function removeExcludedFields(LogRecord $record): array private function removeExcludedFields(array $record): array
{ {
$recordData = $record->toArray();
foreach ($this->excludeFields as $field) { foreach ($this->excludeFields as $field) {
$keys = explode('.', $field); $keys = explode('.', $field);
$node = &$recordData; $node = &$record;
$lastKey = end($keys); $lastKey = end($keys);
foreach ($keys as $key) { foreach ($keys as $key) {
if (!isset($node[$key])) { if (!isset($node[$key])) {
@ -362,6 +382,6 @@ class SlackRecord
} }
} }
return $recordData; return $record;
} }
} }

View file

@ -12,28 +12,31 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\Handler\Slack\SlackRecord; use Monolog\Handler\Slack\SlackRecord;
use Monolog\LogRecord;
/** /**
* Sends notifications through Slack API * Sends notifications through Slack API
* *
* @author Greg Kedzierski <greg@gregkedzierski.com> * @author Greg Kedzierski <greg@gregkedzierski.com>
* @see https://api.slack.com/ * @see https://api.slack.com/
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class SlackHandler extends SocketHandler class SlackHandler extends SocketHandler
{ {
/** /**
* Slack API token * Slack API token
* @var string
*/ */
private string $token; private $token;
/** /**
* Instance of the SlackRecord util class preparing data for Slack API. * Instance of the SlackRecord util class preparing data for Slack API.
* @var SlackRecord
*/ */
private SlackRecord $slackRecord; private $slackRecord;
/** /**
* @param string $token Slack API token * @param string $token Slack API token
@ -52,11 +55,11 @@ class SlackHandler extends SocketHandler
?string $username = null, ?string $username = null,
bool $useAttachment = true, bool $useAttachment = true,
?string $iconEmoji = null, ?string $iconEmoji = null,
$level = Level::Critical, $level = Logger::CRITICAL,
bool $bubble = true, bool $bubble = true,
bool $useShortAttachment = false, bool $useShortAttachment = false,
bool $includeContextAndExtra = false, bool $includeContextAndExtra = false,
array $excludeFields = [], array $excludeFields = array(),
bool $persistent = false, bool $persistent = false,
float $timeout = 0.0, float $timeout = 0.0,
float $writingTimeout = 10.0, float $writingTimeout = 10.0,
@ -102,9 +105,9 @@ class SlackHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function generateDataStream(LogRecord $record): string protected function generateDataStream(array $record): string
{ {
$content = $this->buildContent($record); $content = $this->buildContent($record);
@ -113,8 +116,10 @@ class SlackHandler extends SocketHandler
/** /**
* Builds the body of API call * Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/ */
private function buildContent(LogRecord $record): string private function buildContent(array $record): string
{ {
$dataArray = $this->prepareContentData($record); $dataArray = $this->prepareContentData($record);
@ -122,14 +127,15 @@ class SlackHandler extends SocketHandler
} }
/** /**
* @phpstan-param FormattedRecord $record
* @return string[] * @return string[]
*/ */
protected function prepareContentData(LogRecord $record): array protected function prepareContentData(array $record): array
{ {
$dataArray = $this->slackRecord->getSlackData($record); $dataArray = $this->slackRecord->getSlackData($record);
$dataArray['token'] = $this->token; $dataArray['token'] = $this->token;
if (isset($dataArray['attachments']) && is_array($dataArray['attachments']) && \count($dataArray['attachments']) > 0) { if (!empty($dataArray['attachments'])) {
$dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']); $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']);
} }
@ -151,9 +157,9 @@ class SlackHandler extends SocketHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
parent::write($record); parent::write($record);
$this->finalizeWrite(); $this->finalizeWrite();

View file

@ -12,10 +12,9 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\Handler\Slack\SlackRecord; use Monolog\Handler\Slack\SlackRecord;
use Monolog\LogRecord;
/** /**
* Sends notifications through Slack Webhooks * Sends notifications through Slack Webhooks
@ -27,13 +26,15 @@ class SlackWebhookHandler extends AbstractProcessingHandler
{ {
/** /**
* Slack Webhook token * Slack Webhook token
* @var string
*/ */
private string $webhookUrl; private $webhookUrl;
/** /**
* Instance of the SlackRecord util class preparing data for Slack API. * Instance of the SlackRecord util class preparing data for Slack API.
* @var SlackRecord
*/ */
private SlackRecord $slackRecord; private $slackRecord;
/** /**
* @param string $webhookUrl Slack Webhook URL * @param string $webhookUrl Slack Webhook URL
@ -53,9 +54,9 @@ class SlackWebhookHandler extends AbstractProcessingHandler
?string $iconEmoji = null, ?string $iconEmoji = null,
bool $useShortAttachment = false, bool $useShortAttachment = false,
bool $includeContextAndExtra = false, bool $includeContextAndExtra = false,
$level = Level::Critical, $level = Logger::CRITICAL,
bool $bubble = true, bool $bubble = true,
array $excludeFields = [] array $excludeFields = array()
) { ) {
if (!extension_loaded('curl')) { if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler'); throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler');
@ -87,21 +88,21 @@ class SlackWebhookHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$postData = $this->slackRecord->getSlackData($record); $postData = $this->slackRecord->getSlackData($record);
$postString = Utils::jsonEncode($postData); $postString = Utils::jsonEncode($postData);
$ch = curl_init(); $ch = curl_init();
$options = [ $options = array(
CURLOPT_URL => $this->webhookUrl, CURLOPT_URL => $this->webhookUrl,
CURLOPT_POST => true, CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-type: application/json'], CURLOPT_HTTPHEADER => array('Content-type: application/json'),
CURLOPT_POSTFIELDS => $postString, CURLOPT_POSTFIELDS => $postString,
]; );
if (defined('CURLOPT_SAFE_UPLOAD')) { if (defined('CURLOPT_SAFE_UPLOAD')) {
$options[CURLOPT_SAFE_UPLOAD] = true; $options[CURLOPT_SAFE_UPLOAD] = true;
} }

View file

@ -11,29 +11,41 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Stores to any socket - uses fsockopen() or pfsockopen(). * Stores to any socket - uses fsockopen() or pfsockopen().
* *
* @author Pablo de Leon Belloc <pablolb@gmail.com> * @author Pablo de Leon Belloc <pablolb@gmail.com>
* @see http://php.net/manual/en/function.fsockopen.php * @see http://php.net/manual/en/function.fsockopen.php
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class SocketHandler extends AbstractProcessingHandler class SocketHandler extends AbstractProcessingHandler
{ {
private string $connectionString; /** @var string */
private float $connectionTimeout; private $connectionString;
/** @var float */
private $connectionTimeout;
/** @var resource|null */ /** @var resource|null */
private $resource; private $resource;
private float $timeout; /** @var float */
private float $writingTimeout; private $timeout;
private int|null $lastSentBytes = null; /** @var float */
private int|null $chunkSize; private $writingTimeout;
private bool $persistent; /** @var ?int */
private int|null $errno = null; private $lastSentBytes = null;
private string|null $errstr = null; /** @var ?int */
private float|null $lastWritingAt = null; private $chunkSize;
/** @var bool */
private $persistent;
/** @var ?int */
private $errno = null;
/** @var ?string */
private $errstr = null;
/** @var ?float */
private $lastWritingAt = null;
/** /**
* @param string $connectionString Socket connection string * @param string $connectionString Socket connection string
@ -48,7 +60,7 @@ class SocketHandler extends AbstractProcessingHandler
*/ */
public function __construct( public function __construct(
string $connectionString, string $connectionString,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
bool $persistent = false, bool $persistent = false,
float $timeout = 0.0, float $timeout = 0.0,
@ -75,12 +87,12 @@ class SocketHandler extends AbstractProcessingHandler
/** /**
* Connect (if necessary) and write to the socket * Connect (if necessary) and write to the socket
* *
* @inheritDoc * {@inheritDoc}
* *
* @throws \UnexpectedValueException * @throws \UnexpectedValueException
* @throws \RuntimeException * @throws \RuntimeException
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->connectIfNotConnected(); $this->connectIfNotConnected();
$data = $this->generateDataStream($record); $data = $this->generateDataStream($record);
@ -201,6 +213,8 @@ class SocketHandler extends AbstractProcessingHandler
/** /**
* Get current local writing timeout * Get current local writing timeout
*
* @return float
*/ */
public function getWritingTimeout(): float public function getWritingTimeout(): float
{ {
@ -250,8 +264,10 @@ class SocketHandler extends AbstractProcessingHandler
* Wrapper to allow mocking * Wrapper to allow mocking
* *
* @see http://php.net/manual/en/function.stream-set-timeout.php * @see http://php.net/manual/en/function.stream-set-timeout.php
*
* @return bool
*/ */
protected function streamSetTimeout(): bool protected function streamSetTimeout()
{ {
$seconds = floor($this->timeout); $seconds = floor($this->timeout);
$microseconds = round(($this->timeout - $seconds) * 1e6); $microseconds = round(($this->timeout - $seconds) * 1e6);
@ -268,9 +284,9 @@ class SocketHandler extends AbstractProcessingHandler
* *
* @see http://php.net/manual/en/function.stream-set-chunk-size.php * @see http://php.net/manual/en/function.stream-set-chunk-size.php
* *
* @return int|false * @return int|bool
*/ */
protected function streamSetChunkSize(): int|bool protected function streamSetChunkSize()
{ {
if (!is_resource($this->resource)) { if (!is_resource($this->resource)) {
throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource'); throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource');
@ -286,9 +302,9 @@ class SocketHandler extends AbstractProcessingHandler
/** /**
* Wrapper to allow mocking * Wrapper to allow mocking
* *
* @return int|false * @return int|bool
*/ */
protected function fwrite(string $data): int|bool protected function fwrite(string $data)
{ {
if (!is_resource($this->resource)) { if (!is_resource($this->resource)) {
throw new \LogicException('fwrite called but $this->resource is not a resource'); throw new \LogicException('fwrite called but $this->resource is not a resource');
@ -302,7 +318,7 @@ class SocketHandler extends AbstractProcessingHandler
* *
* @return mixed[]|bool * @return mixed[]|bool
*/ */
protected function streamGetMetadata(): array|bool protected function streamGetMetadata()
{ {
if (!is_resource($this->resource)) { if (!is_resource($this->resource)) {
throw new \LogicException('streamGetMetadata called but $this->resource is not a resource'); throw new \LogicException('streamGetMetadata called but $this->resource is not a resource');
@ -326,9 +342,12 @@ class SocketHandler extends AbstractProcessingHandler
$this->connect(); $this->connect();
} }
protected function generateDataStream(LogRecord $record): string /**
* @phpstan-param FormattedRecord $record
*/
protected function generateDataStream(array $record): string
{ {
return (string) $record->formatted; return (string) $record['formatted'];
} }
/** /**
@ -368,7 +387,7 @@ class SocketHandler extends AbstractProcessingHandler
private function setStreamChunkSize(): void private function setStreamChunkSize(): void
{ {
if (null !== $this->chunkSize && false === $this->streamSetChunkSize()) { if ($this->chunkSize && !$this->streamSetChunkSize()) {
throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
} }
} }
@ -389,7 +408,7 @@ class SocketHandler extends AbstractProcessingHandler
} }
$sent += $chunk; $sent += $chunk;
$socketInfo = $this->streamGetMetadata(); $socketInfo = $this->streamGetMetadata();
if (is_array($socketInfo) && (bool) $socketInfo['timed_out']) { if (is_array($socketInfo) && $socketInfo['timed_out']) {
throw new \RuntimeException("Write timed-out"); throw new \RuntimeException("Write timed-out");
} }
@ -418,7 +437,7 @@ class SocketHandler extends AbstractProcessingHandler
usleep(100); usleep(100);
} }
if ((microtime(true) - (float) $this->lastWritingAt) >= $this->writingTimeout) { if ((microtime(true) - $this->lastWritingAt) >= $this->writingTimeout) {
$this->closeSocket(); $this->closeSocket();
return true; return true;

View file

@ -12,9 +12,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Aws\Sqs\SqsClient; use Aws\Sqs\SqsClient;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Writes to any sqs queue. * Writes to any sqs queue.
@ -28,10 +27,12 @@ class SqsHandler extends AbstractProcessingHandler
/** 100 KB in bytes - head message size for new error log */ /** 100 KB in bytes - head message size for new error log */
protected const HEAD_MESSAGE_SIZE = 102400; protected const HEAD_MESSAGE_SIZE = 102400;
private SqsClient $client; /** @var SqsClient */
private string $queueUrl; private $client;
/** @var string */
private $queueUrl;
public function __construct(SqsClient $sqsClient, string $queueUrl, int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct(SqsClient $sqsClient, string $queueUrl, $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -40,15 +41,15 @@ class SqsHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!isset($record->formatted) || 'string' !== gettype($record->formatted)) { if (!isset($record['formatted']) || 'string' !== gettype($record['formatted'])) {
throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record)); throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record));
} }
$messageBody = $record->formatted; $messageBody = $record['formatted'];
if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) { if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) {
$messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE); $messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE);
} }

View file

@ -11,9 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Stores to any stream resource * Stores to any stream resource
@ -21,21 +20,29 @@ use Monolog\LogRecord;
* Can be used to store into php://stderr, remote and local files, etc. * Can be used to store into php://stderr, remote and local files, etc.
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class StreamHandler extends AbstractProcessingHandler class StreamHandler extends AbstractProcessingHandler
{ {
/** @const int */
protected const MAX_CHUNK_SIZE = 2147483647; protected const MAX_CHUNK_SIZE = 2147483647;
/** 10MB */ /** @const int 10MB */
protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024; protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024;
protected int $streamChunkSize; /** @var int */
protected $streamChunkSize;
/** @var resource|null */ /** @var resource|null */
protected $stream; protected $stream;
protected string|null $url = null; /** @var ?string */
private string|null $errorMessage = null; protected $url = null;
protected int|null $filePermission; /** @var ?string */
protected bool $useLocking; private $errorMessage = null;
/** @var ?int */
protected $filePermission;
/** @var bool */
protected $useLocking;
/** @var true|null */ /** @var true|null */
private bool|null $dirCreated = null; private $dirCreated = null;
/** /**
* @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write * @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write
@ -44,7 +51,7 @@ class StreamHandler extends AbstractProcessingHandler
* *
* @throws \InvalidArgumentException If stream is not a resource or string * @throws \InvalidArgumentException If stream is not a resource or string
*/ */
public function __construct($stream, int|string|Level $level = Level::Debug, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -76,11 +83,11 @@ class StreamHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
if (null !== $this->url && is_resource($this->stream)) { if ($this->url && is_resource($this->stream)) {
fclose($this->stream); fclose($this->stream);
} }
$this->stream = null; $this->stream = null;
@ -99,21 +106,26 @@ class StreamHandler extends AbstractProcessingHandler
/** /**
* Return the stream URL if it was configured with a URL and not an active resource * Return the stream URL if it was configured with a URL and not an active resource
*
* @return string|null
*/ */
public function getUrl(): ?string public function getUrl(): ?string
{ {
return $this->url; return $this->url;
} }
/**
* @return int
*/
public function getStreamChunkSize(): int public function getStreamChunkSize(): int
{ {
return $this->streamChunkSize; return $this->streamChunkSize;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!is_resource($this->stream)) { if (!is_resource($this->stream)) {
$url = $this->url; $url = $this->url;
@ -138,6 +150,10 @@ class StreamHandler extends AbstractProcessingHandler
} }
$stream = $this->stream; $stream = $this->stream;
if (!is_resource($stream)) {
throw new \LogicException('No stream was opened yet' . Utils::getRecordMessageForException($record));
}
if ($this->useLocking) { if ($this->useLocking) {
// ignoring errors here, there's not much we can do about them // ignoring errors here, there's not much we can do about them
flock($stream, LOCK_EX); flock($stream, LOCK_EX);
@ -153,10 +169,13 @@ class StreamHandler extends AbstractProcessingHandler
/** /**
* Write to stream * Write to stream
* @param resource $stream * @param resource $stream
* @param array $record
*
* @phpstan-param FormattedRecord $record
*/ */
protected function streamWrite($stream, LogRecord $record): void protected function streamWrite($stream, array $record): void
{ {
fwrite($stream, (string) $record->formatted); fwrite($stream, (string) $record['formatted']);
} }
private function customErrorHandler(int $code, string $msg): bool private function customErrorHandler(int $code, string $msg): bool
@ -183,7 +202,7 @@ class StreamHandler extends AbstractProcessingHandler
private function createDir(string $url): void private function createDir(string $url): void
{ {
// Do not try to create dir if it has already been tried. // Do not try to create dir if it has already been tried.
if (true === $this->dirCreated) { if ($this->dirCreated) {
return; return;
} }

View file

@ -11,10 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Closure;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Monolog\LogRecord;
use Monolog\Utils; use Monolog\Utils;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
@ -26,20 +23,23 @@ use Symfony\Component\Mime\Email;
* SymfonyMailerHandler uses Symfony's Mailer component to send the emails * SymfonyMailerHandler uses Symfony's Mailer component to send the emails
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class SymfonyMailerHandler extends MailHandler class SymfonyMailerHandler extends MailHandler
{ {
protected MailerInterface|TransportInterface $mailer; /** @var MailerInterface|TransportInterface */
/** @var Email|Closure(string, LogRecord[]): Email */ protected $mailer;
private Email|Closure $emailTemplate; /** @var Email|callable(string, Record[]): Email */
private $emailTemplate;
/** /**
* @phpstan-param Email|Closure(string, LogRecord[]): Email $email * @psalm-param Email|callable(string, Record[]): Email $email
* *
* @param MailerInterface|TransportInterface $mailer The mailer to use * @param MailerInterface|TransportInterface $mailer The mailer to use
* @param Closure|Email $email An email template, the subject/body will be replaced * @param callable|Email $email An email template, the subject/body will be replaced
*/ */
public function __construct($mailer, Email|Closure $email, int|string|Level $level = Level::Error, bool $bubble = true) public function __construct($mailer, $email, $level = Logger::ERROR, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -69,7 +69,9 @@ class SymfonyMailerHandler extends MailHandler
* Creates instance of Email to be sent * Creates instance of Email to be sent
* *
* @param string $content formatted email body to be sent * @param string $content formatted email body to be sent
* @param LogRecord[] $records Log records that formed the content * @param array $records Log records that formed the content
*
* @phpstan-param Record[] $records
*/ */
protected function buildMessage(string $content, array $records): Email protected function buildMessage(string $content, array $records): Email
{ {
@ -82,10 +84,10 @@ class SymfonyMailerHandler extends MailHandler
if (!$message instanceof Email) { if (!$message instanceof Email) {
$record = reset($records); $record = reset($records);
throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record instanceof LogRecord ? Utils::getRecordMessageForException($record) : '')); throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : ''));
} }
if (\count($records) > 0) { if ($records) {
$subjectFormatter = $this->getSubjectFormatter($message->getSubject()); $subjectFormatter = $this->getSubjectFormatter($message->getSubject());
$message->subject($subjectFormatter->format($this->getHighestRecord($records))); $message->subject($subjectFormatter->format($this->getHighestRecord($records)));
} }

View file

@ -11,9 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Logs to syslog service. * Logs to syslog service.
@ -30,14 +29,17 @@ use Monolog\LogRecord;
*/ */
class SyslogHandler extends AbstractSyslogHandler class SyslogHandler extends AbstractSyslogHandler
{ {
protected string $ident; /** @var string */
protected int $logopts; protected $ident;
/** @var int */
protected $logopts;
/** /**
* @param string $ident
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
* @param int $logopts Option flags for the openlog() call, defaults to LOG_PID * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID
*/ */
public function __construct(string $ident, string|int $facility = LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true, int $logopts = LOG_PID) public function __construct(string $ident, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, int $logopts = LOG_PID)
{ {
parent::__construct($facility, $level, $bubble); parent::__construct($facility, $level, $bubble);
@ -46,7 +48,7 @@ class SyslogHandler extends AbstractSyslogHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function close(): void public function close(): void
{ {
@ -54,13 +56,13 @@ class SyslogHandler extends AbstractSyslogHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
if (!openlog($this->ident, $this->logopts, $this->facility)) { if (!openlog($this->ident, $this->logopts, $this->facility)) {
throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"' . Utils::getRecordMessageForException($record)); throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"' . Utils::getRecordMessageForException($record));
} }
syslog($this->toSyslogPriority($record->level), (string) $record->formatted); syslog($this->logLevels[$record['level']], (string) $record['formatted']);
} }
} }

View file

@ -18,9 +18,12 @@ class UdpSocket
{ {
protected const DATAGRAM_MAX_LENGTH = 65023; protected const DATAGRAM_MAX_LENGTH = 65023;
protected string $ip; /** @var string */
protected int $port; protected $ip;
protected ?Socket $socket = null; /** @var int */
protected $port;
/** @var resource|Socket|null */
protected $socket = null;
public function __construct(string $ip, int $port = 514) public function __construct(string $ip, int $port = 514)
{ {
@ -28,20 +31,28 @@ class UdpSocket
$this->port = $port; $this->port = $port;
} }
public function write(string $line, string $header = ""): void /**
* @param string $line
* @param string $header
* @return void
*/
public function write($line, $header = "")
{ {
$this->send($this->assembleMessage($line, $header)); $this->send($this->assembleMessage($line, $header));
} }
public function close(): void public function close(): void
{ {
if ($this->socket instanceof Socket) { if (is_resource($this->socket) || $this->socket instanceof Socket) {
socket_close($this->socket); socket_close($this->socket);
$this->socket = null; $this->socket = null;
} }
} }
protected function getSocket(): Socket /**
* @return resource|Socket
*/
protected function getSocket()
{ {
if (null !== $this->socket) { if (null !== $this->socket) {
return $this->socket; return $this->socket;
@ -55,12 +66,12 @@ class UdpSocket
$protocol = IPPROTO_IP; $protocol = IPPROTO_IP;
} }
$socket = socket_create($domain, SOCK_DGRAM, $protocol); $this->socket = socket_create($domain, SOCK_DGRAM, $protocol) ?: null;
if ($socket instanceof Socket) { if (null === $this->socket) {
return $this->socket = $socket; throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' could not be opened via socket_create');
} }
throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' could not be opened via socket_create'); return $this->socket;
} }
protected function send(string $chunk): void protected function send(string $chunk): void

View file

@ -12,9 +12,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use DateTimeInterface; use DateTimeInterface;
use Monolog\Logger;
use Monolog\Handler\SyslogUdp\UdpSocket; use Monolog\Handler\SyslogUdp\UdpSocket;
use Monolog\Level;
use Monolog\LogRecord;
use Monolog\Utils; use Monolog\Utils;
/** /**
@ -30,16 +29,18 @@ class SyslogUdpHandler extends AbstractSyslogHandler
const RFC5424e = 2; const RFC5424e = 2;
/** @var array<self::RFC*, string> */ /** @var array<self::RFC*, string> */
private array $dateFormats = [ private $dateFormats = array(
self::RFC3164 => 'M d H:i:s', self::RFC3164 => 'M d H:i:s',
self::RFC5424 => \DateTime::RFC3339, self::RFC5424 => \DateTime::RFC3339,
self::RFC5424e => \DateTime::RFC3339_EXTENDED, self::RFC5424e => \DateTime::RFC3339_EXTENDED,
]; );
protected UdpSocket $socket; /** @var UdpSocket */
protected string $ident; protected $socket;
/** @var string */
protected $ident;
/** @var self::RFC* */ /** @var self::RFC* */
protected int $rfc; protected $rfc;
/** /**
* @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then) * @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then)
@ -52,7 +53,7 @@ class SyslogUdpHandler extends AbstractSyslogHandler
* *
* @phpstan-param self::RFC* $rfc * @phpstan-param self::RFC* $rfc
*/ */
public function __construct(string $host, int $port = 514, string|int $facility = LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424) public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424)
{ {
if (!extension_loaded('sockets')) { if (!extension_loaded('sockets')) {
throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler'); throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler');
@ -66,11 +67,11 @@ class SyslogUdpHandler extends AbstractSyslogHandler
$this->socket = new UdpSocket($host, $port); $this->socket = new UdpSocket($host, $port);
} }
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$lines = $this->splitMessageIntoLines($record->formatted); $lines = $this->splitMessageIntoLines($record['formatted']);
$header = $this->makeCommonSyslogHeader($this->toSyslogPriority($record->level), $record->datetime); $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']], $record['datetime']);
foreach ($lines as $line) { foreach ($lines as $line) {
$this->socket->write($line, $header); $this->socket->write($line, $header);
@ -95,7 +96,6 @@ class SyslogUdpHandler extends AbstractSyslogHandler
$lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY); $lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY);
if (false === $lines) { if (false === $lines) {
$pcreErrorCode = preg_last_error(); $pcreErrorCode = preg_last_error();
throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
} }
@ -109,13 +109,11 @@ class SyslogUdpHandler extends AbstractSyslogHandler
{ {
$priority = $severity + $this->facility; $priority = $severity + $this->facility;
$pid = getmypid(); if (!$pid = getmypid()) {
if (false === $pid) {
$pid = '-'; $pid = '-';
} }
$hostname = gethostname(); if (!$hostname = gethostname()) {
if (false === $hostname) {
$hostname = '-'; $hostname = '-';
} }

View file

@ -12,9 +12,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use RuntimeException; use RuntimeException;
use Monolog\Level; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
use Monolog\LogRecord;
/** /**
* Handler send logs to Telegram using Telegram Bot API. * Handler send logs to Telegram using Telegram Bot API.
@ -29,6 +28,8 @@ use Monolog\LogRecord;
* @link https://core.telegram.org/bots/api * @link https://core.telegram.org/bots/api
* *
* @author Mazur Alexandr <alexandrmazur96@gmail.com> * @author Mazur Alexandr <alexandrmazur96@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class TelegramBotHandler extends AbstractProcessingHandler class TelegramBotHandler extends AbstractProcessingHandler
{ {
@ -51,42 +52,49 @@ class TelegramBotHandler extends AbstractProcessingHandler
/** /**
* Telegram bot access token provided by BotFather. * Telegram bot access token provided by BotFather.
* Create telegram bot with https://telegram.me/BotFather and use access token from it. * Create telegram bot with https://telegram.me/BotFather and use access token from it.
* @var string
*/ */
private string $apiKey; private $apiKey;
/** /**
* Telegram channel name. * Telegram channel name.
* Since to start with '@' symbol as prefix. * Since to start with '@' symbol as prefix.
* @var string
*/ */
private string $channel; private $channel;
/** /**
* The kind of formatting that is used for the message. * The kind of formatting that is used for the message.
* See available options at https://core.telegram.org/bots/api#formatting-options * See available options at https://core.telegram.org/bots/api#formatting-options
* or in AVAILABLE_PARSE_MODES * or in AVAILABLE_PARSE_MODES
* @var ?string
*/ */
private string|null $parseMode; private $parseMode;
/** /**
* Disables link previews for links in the message. * Disables link previews for links in the message.
* @var ?bool
*/ */
private bool|null $disableWebPagePreview; private $disableWebPagePreview;
/** /**
* Sends the message silently. Users will receive a notification with no sound. * Sends the message silently. Users will receive a notification with no sound.
* @var ?bool
*/ */
private bool|null $disableNotification; private $disableNotification;
/** /**
* True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages.
* False - truncates a message that is too long. * False - truncates a message that is too long.
* @var bool
*/ */
private bool $splitLongMessages; private $splitLongMessages;
/** /**
* Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests).
* @var bool
*/ */
private bool $delayBetweenMessages; private $delayBetweenMessages;
/** /**
* @param string $apiKey Telegram bot access token provided by BotFather * @param string $apiKey Telegram bot access token provided by BotFather
@ -98,14 +106,15 @@ class TelegramBotHandler extends AbstractProcessingHandler
public function __construct( public function __construct(
string $apiKey, string $apiKey,
string $channel, string $channel,
$level = Level::Debug, $level = Logger::DEBUG,
bool $bubble = true, bool $bubble = true,
string $parseMode = null, string $parseMode = null,
bool $disableWebPagePreview = null, bool $disableWebPagePreview = null,
bool $disableNotification = null, bool $disableNotification = null,
bool $splitLongMessages = false, bool $splitLongMessages = false,
bool $delayBetweenMessages = false bool $delayBetweenMessages = false
) { )
{
if (!extension_loaded('curl')) { if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler'); throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler');
} }
@ -123,7 +132,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
public function setParseMode(string $parseMode = null): self public function setParseMode(string $parseMode = null): self
{ {
if ($parseMode !== null && !in_array($parseMode, self::AVAILABLE_PARSE_MODES, true)) { if ($parseMode !== null && !in_array($parseMode, self::AVAILABLE_PARSE_MODES)) {
throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . implode(', ', self::AVAILABLE_PARSE_MODES) . '.'); throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . implode(', ', self::AVAILABLE_PARSE_MODES) . '.');
} }
@ -149,6 +158,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
/** /**
* True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages.
* False - truncates a message that is too long. * False - truncates a message that is too long.
* @param bool $splitLongMessages
* @return $this * @return $this
*/ */
public function splitLongMessages(bool $splitLongMessages = false): self public function splitLongMessages(bool $splitLongMessages = false): self
@ -160,6 +170,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
/** /**
* Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests).
* @param bool $delayBetweenMessages
* @return $this * @return $this
*/ */
public function delayBetweenMessages(bool $delayBetweenMessages = false): self public function delayBetweenMessages(bool $delayBetweenMessages = false): self
@ -170,10 +181,11 @@ class TelegramBotHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
/** @var Record[] $messages */
$messages = []; $messages = [];
foreach ($records as $record) { foreach ($records as $record) {
@ -181,28 +193,30 @@ class TelegramBotHandler extends AbstractProcessingHandler
continue; continue;
} }
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
$messages[] = $record; $messages[] = $record;
} }
if (\count($messages) > 0) { if (!empty($messages)) {
$this->send((string) $this->getFormatter()->formatBatch($messages)); $this->send((string)$this->getFormatter()->formatBatch($messages));
} }
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->send($record->formatted); $this->send($record['formatted']);
} }
/** /**
* Send request to @link https://api.telegram.org/bot on SendMessage action. * Send request to @link https://api.telegram.org/bot on SendMessage action.
* @param string $message
*/ */
protected function send(string $message): void protected function send(string $message): void
{ {
@ -245,6 +259,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
/** /**
* Handle a message that is too long: truncates or splits into several * Handle a message that is too long: truncates or splits into several
* @param string $message
* @return string[] * @return string[]
*/ */
private function handleMessageLength(string $message): array private function handleMessageLength(string $message): array

View file

@ -11,10 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\LogRecord;
/** /**
* Used for testing purposes. * Used for testing purposes.
@ -67,67 +65,85 @@ use Monolog\LogRecord;
* @method bool hasNoticeThatPasses($message) * @method bool hasNoticeThatPasses($message)
* @method bool hasInfoThatPasses($message) * @method bool hasInfoThatPasses($message)
* @method bool hasDebugThatPasses($message) * @method bool hasDebugThatPasses($message)
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/ */
class TestHandler extends AbstractProcessingHandler class TestHandler extends AbstractProcessingHandler
{ {
/** @var LogRecord[] */ /** @var Record[] */
protected array $records = []; protected $records = [];
/** @phpstan-var array<value-of<Level::VALUES>, LogRecord[]> */ /** @var array<Level, Record[]> */
protected array $recordsByLevel = []; protected $recordsByLevel = [];
private bool $skipReset = false; /** @var bool */
private $skipReset = false;
/** /**
* @return array<LogRecord> * @return array
*
* @phpstan-return Record[]
*/ */
public function getRecords(): array public function getRecords()
{ {
return $this->records; return $this->records;
} }
public function clear(): void /**
* @return void
*/
public function clear()
{ {
$this->records = []; $this->records = [];
$this->recordsByLevel = []; $this->recordsByLevel = [];
} }
public function reset(): void /**
* @return void
*/
public function reset()
{ {
if (!$this->skipReset) { if (!$this->skipReset) {
$this->clear(); $this->clear();
} }
} }
public function setSkipReset(bool $skipReset): void /**
* @return void
*/
public function setSkipReset(bool $skipReset)
{ {
$this->skipReset = $skipReset; $this->skipReset = $skipReset;
} }
/** /**
* @param int|string|Level|LogLevel::* $level Logging level value or name * @param string|int $level Logging level value or name
* *
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level * @phpstan-param Level|LevelName|LogLevel::* $level
*/ */
public function hasRecords(int|string|Level $level): bool public function hasRecords($level): bool
{ {
return isset($this->recordsByLevel[Logger::toMonologLevel($level)->value]); return isset($this->recordsByLevel[Logger::toMonologLevel($level)]);
} }
/** /**
* @param string|array $recordAssertions Either a message string or an array containing message and optionally context keys that will be checked against all records * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records
* @param string|int $level Logging level value or name
* *
* @phpstan-param array{message: string, context?: mixed[]}|string $recordAssertions * @phpstan-param array{message: string, context?: mixed[]}|string $record
* @phpstan-param Level|LevelName|LogLevel::* $level
*/ */
public function hasRecord(string|array $recordAssertions, Level $level): bool public function hasRecord($record, $level): bool
{ {
if (is_string($recordAssertions)) { if (is_string($record)) {
$recordAssertions = ['message' => $recordAssertions]; $record = array('message' => $record);
} }
return $this->hasRecordThatPasses(function (LogRecord $rec) use ($recordAssertions) { return $this->hasRecordThatPasses(function ($rec) use ($record) {
if ($rec->message !== $recordAssertions['message']) { if ($rec['message'] !== $record['message']) {
return false; return false;
} }
if (isset($recordAssertions['context']) && $rec->context !== $recordAssertions['context']) { if (isset($record['context']) && $rec['context'] !== $record['context']) {
return false; return false;
} }
@ -135,29 +151,47 @@ class TestHandler extends AbstractProcessingHandler
}, $level); }, $level);
} }
public function hasRecordThatContains(string $message, Level $level): bool /**
* @param string|int $level Logging level value or name
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecordThatContains(string $message, $level): bool
{ {
return $this->hasRecordThatPasses(fn (LogRecord $rec) => str_contains($rec->message, $message), $level); return $this->hasRecordThatPasses(function ($rec) use ($message) {
} return strpos($rec['message'], $message) !== false;
}, $level);
public function hasRecordThatMatches(string $regex, Level $level): bool
{
return $this->hasRecordThatPasses(fn (LogRecord $rec) => preg_match($regex, $rec->message) > 0, $level);
} }
/** /**
* @phpstan-param callable(LogRecord, int): mixed $predicate * @param string|int $level Logging level value or name
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/ */
public function hasRecordThatPasses(callable $predicate, Level $level): bool public function hasRecordThatMatches(string $regex, $level): bool
{
return $this->hasRecordThatPasses(function (array $rec) use ($regex): bool {
return preg_match($regex, $rec['message']) > 0;
}, $level);
}
/**
* @param string|int $level Logging level value or name
* @return bool
*
* @psalm-param callable(Record, int): mixed $predicate
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecordThatPasses(callable $predicate, $level)
{ {
$level = Logger::toMonologLevel($level); $level = Logger::toMonologLevel($level);
if (!isset($this->recordsByLevel[$level->value])) { if (!isset($this->recordsByLevel[$level])) {
return false; return false;
} }
foreach ($this->recordsByLevel[$level->value] as $i => $rec) { foreach ($this->recordsByLevel[$level] as $i => $rec) {
if ((bool) $predicate($rec, $i)) { if ($predicate($rec, $i)) {
return true; return true;
} }
} }
@ -166,22 +200,24 @@ class TestHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
protected function write(LogRecord $record): void protected function write(array $record): void
{ {
$this->recordsByLevel[$record->level->value][] = $record; $this->recordsByLevel[$record['level']][] = $record;
$this->records[] = $record; $this->records[] = $record;
} }
/** /**
* @param string $method
* @param mixed[] $args * @param mixed[] $args
* @return bool
*/ */
public function __call(string $method, array $args): bool public function __call($method, $args)
{ {
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
$genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
$level = constant(Level::class.'::' . $matches[2]); $level = constant('Monolog\Logger::' . strtoupper($matches[2]));
$callback = [$this, $genericMethod]; $callback = [$this, $genericMethod];
if (is_callable($callback)) { if (is_callable($callback)) {
$args[] = $level; $args[] = $level;

View file

@ -15,6 +15,7 @@ trait WebRequestRecognizerTrait
{ {
/** /**
* Checks if PHP's serving a web request * Checks if PHP's serving a web request
* @return bool
*/ */
protected function isWebRequest(): bool protected function isWebRequest(): bool
{ {

View file

@ -11,30 +11,30 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\LogRecord;
use Throwable;
/** /**
* Forwards records to multiple handlers suppressing failures of each handler * Forwards records to multiple handlers suppressing failures of each handler
* and continuing through to give every handler a chance to succeed. * and continuing through to give every handler a chance to succeed.
* *
* @author Craig D'Amelio <craig@damelio.ca> * @author Craig D'Amelio <craig@damelio.ca>
*
* @phpstan-import-type Record from \Monolog\Logger
*/ */
class WhatFailureGroupHandler extends GroupHandler class WhatFailureGroupHandler extends GroupHandler
{ {
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handle(LogRecord $record): bool public function handle(array $record): bool
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record); $record = $this->processRecord($record);
} }
foreach ($this->handlers as $handler) { foreach ($this->handlers as $handler) {
try { try {
$handler->handle($record); $handler->handle($record);
} catch (Throwable) { } catch (\Throwable $e) {
// What failure? // What failure?
} }
} }
@ -43,22 +43,23 @@ class WhatFailureGroupHandler extends GroupHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records): void
{ {
if (\count($this->processors) > 0) { if ($this->processors) {
$processed = []; $processed = array();
foreach ($records as $record) { foreach ($records as $record) {
$processed[] = $this->processRecord($record); $processed[] = $this->processRecord($record);
} }
/** @var Record[] $records */
$records = $processed; $records = $processed;
} }
foreach ($this->handlers as $handler) { foreach ($this->handlers as $handler) {
try { try {
$handler->handleBatch($records); $handler->handleBatch($records);
} catch (Throwable) { } catch (\Throwable $e) {
// What failure? // What failure?
} }
} }

View file

@ -13,58 +13,59 @@ namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\NormalizerFormatter; use Monolog\Formatter\NormalizerFormatter;
use Monolog\Level; use Monolog\Logger;
use Monolog\LogRecord;
/** /**
* Handler sending logs to Zend Monitor * Handler sending logs to Zend Monitor
* *
* @author Christian Bergau <cbergau86@gmail.com> * @author Christian Bergau <cbergau86@gmail.com>
* @author Jason Davis <happydude@jasondavis.net> * @author Jason Davis <happydude@jasondavis.net>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/ */
class ZendMonitorHandler extends AbstractProcessingHandler class ZendMonitorHandler extends AbstractProcessingHandler
{ {
/**
* Monolog level / ZendMonitor Custom Event priority map
*
* @var array<int, int>
*/
protected $levelMap = [];
/** /**
* @throws MissingExtensionException * @throws MissingExtensionException
*/ */
public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true) public function __construct($level = Logger::DEBUG, bool $bubble = true)
{ {
if (!function_exists('zend_monitor_custom_event')) { if (!function_exists('zend_monitor_custom_event')) {
throw new MissingExtensionException( throw new MissingExtensionException(
'You must have Zend Server installed with Zend Monitor enabled in order to use this handler' 'You must have Zend Server installed with Zend Monitor enabled in order to use this handler'
); );
} }
//zend monitor constants are not defined if zend monitor is not enabled.
$this->levelMap = [
Logger::DEBUG => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
Logger::INFO => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
Logger::NOTICE => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
Logger::WARNING => \ZEND_MONITOR_EVENT_SEVERITY_WARNING,
Logger::ERROR => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
Logger::CRITICAL => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
Logger::ALERT => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
Logger::EMERGENCY => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
];
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
} }
/** /**
* Translates Monolog log levels to ZendMonitor levels. * {@inheritDoc}
*/ */
protected function toZendMonitorLevel(Level $level): int protected function write(array $record): void
{
return match ($level) {
Level::Debug => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
Level::Info => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
Level::Notice => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
Level::Warning => \ZEND_MONITOR_EVENT_SEVERITY_WARNING,
Level::Error => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
Level::Critical => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
Level::Alert => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
Level::Emergency => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
};
}
/**
* @inheritDoc
*/
protected function write(LogRecord $record): void
{ {
$this->writeZendMonitorCustomEvent( $this->writeZendMonitorCustomEvent(
$record->level->getName(), Logger::getLevelName($record['level']),
$record->message, $record['message'],
$record->formatted, $record['formatted'],
$this->toZendMonitorLevel($record->level) $this->levelMap[$record['level']]
); );
} }
@ -72,8 +73,10 @@ class ZendMonitorHandler extends AbstractProcessingHandler
* Write to Zend Monitor Events * Write to Zend Monitor Events
* @param string $type Text displayed in "Class Name (custom)" field * @param string $type Text displayed in "Class Name (custom)" field
* @param string $message Text displayed in "Error String" * @param string $message Text displayed in "Error String"
* @param array<mixed> $formatted Displayed in Custom Variables tab * @param array $formatted Displayed in Custom Variables tab
* @param int $severity Set the event severity level (-1,0,1) * @param int $severity Set the event severity level (-1,0,1)
*
* @phpstan-param FormattedRecord $formatted
*/ */
protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void
{ {
@ -81,10 +84,18 @@ class ZendMonitorHandler extends AbstractProcessingHandler
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function getDefaultFormatter(): FormatterInterface public function getDefaultFormatter(): FormatterInterface
{ {
return new NormalizerFormatter(); return new NormalizerFormatter();
} }
/**
* @return array<int, int>
*/
public function getLevelMap(): array
{
return $this->levelMap;
}
} }

View file

@ -1,209 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog;
use Psr\Log\LogLevel;
/**
* Represents the log levels
*
* Monolog supports the logging levels described by RFC 5424 {@see https://datatracker.ietf.org/doc/html/rfc5424}
* but due to BC the severity values used internally are not 0-7.
*
* To get the level name out of a Level there are three options:
*
* - Use ->getName() to get the standard Monolog name which is full uppercased (e.g. "DEBUG")
* - Use ->toPsrLogLevel() to get the standard PSR-3 name which is full lowercased (e.g. "debug")
* - Use ->toRFC5424Level() to get the standard RFC 5424 value (e.g. 7 for debug, 0 for emergency)
* - Use ->name to get the enum case's name which is capitalized (e.g. "Debug")
*
* To get the value for filtering, if the includes/isLowerThan/isHigherThan methods are
* not enough, you can use ->value to get the enum case's integer value.
*/
enum Level: int
{
/**
* Detailed debug information
*/
case Debug = 100;
/**
* Interesting events
*
* Examples: User logs in, SQL logs.
*/
case Info = 200;
/**
* Uncommon events
*/
case Notice = 250;
/**
* Exceptional occurrences that are not errors
*
* Examples: Use of deprecated APIs, poor use of an API,
* undesirable things that are not necessarily wrong.
*/
case Warning = 300;
/**
* Runtime errors
*/
case Error = 400;
/**
* Critical conditions
*
* Example: Application component unavailable, unexpected exception.
*/
case Critical = 500;
/**
* Action must be taken immediately
*
* Example: Entire website down, database unavailable, etc.
* This should trigger the SMS alerts and wake you up.
*/
case Alert = 550;
/**
* Urgent alert.
*/
case Emergency = 600;
/**
* @param value-of<self::NAMES>|LogLevel::*|'Debug'|'Info'|'Notice'|'Warning'|'Error'|'Critical'|'Alert'|'Emergency' $name
* @return static
*/
public static function fromName(string $name): self
{
return match ($name) {
'debug', 'Debug', 'DEBUG' => self::Debug,
'info', 'Info', 'INFO' => self::Info,
'notice', 'Notice', 'NOTICE' => self::Notice,
'warning', 'Warning', 'WARNING' => self::Warning,
'error', 'Error', 'ERROR' => self::Error,
'critical', 'Critical', 'CRITICAL' => self::Critical,
'alert', 'Alert', 'ALERT' => self::Alert,
'emergency', 'Emergency', 'EMERGENCY' => self::Emergency,
};
}
/**
* @param value-of<self::VALUES> $value
* @return static
*/
public static function fromValue(int $value): self
{
return self::from($value);
}
/**
* Returns true if the passed $level is higher or equal to $this
*/
public function includes(Level $level): bool
{
return $this->value <= $level->value;
}
public function isHigherThan(Level $level): bool
{
return $this->value > $level->value;
}
public function isLowerThan(Level $level): bool
{
return $this->value < $level->value;
}
/**
* Returns the monolog standardized all-capitals name of the level
*
* Use this instead of $level->name which returns the enum case name (e.g. Debug vs DEBUG if you use getName())
*
* @return value-of<self::NAMES>
*/
public function getName(): string
{
return match ($this) {
self::Debug => 'DEBUG',
self::Info => 'INFO',
self::Notice => 'NOTICE',
self::Warning => 'WARNING',
self::Error => 'ERROR',
self::Critical => 'CRITICAL',
self::Alert => 'ALERT',
self::Emergency => 'EMERGENCY',
};
}
/**
* Returns the PSR-3 level matching this instance
*
* @phpstan-return \Psr\Log\LogLevel::*
*/
public function toPsrLogLevel(): string
{
return match ($this) {
self::Debug => LogLevel::DEBUG,
self::Info => LogLevel::INFO,
self::Notice => LogLevel::NOTICE,
self::Warning => LogLevel::WARNING,
self::Error => LogLevel::ERROR,
self::Critical => LogLevel::CRITICAL,
self::Alert => LogLevel::ALERT,
self::Emergency => LogLevel::EMERGENCY,
};
}
/**
* Returns the RFC 5424 level matching this instance
*
* @phpstan-return int<0, 7>
*/
public function toRFC5424Level(): int
{
return match ($this) {
self::Debug => 7,
self::Info => 6,
self::Notice => 5,
self::Warning => 4,
self::Error => 3,
self::Critical => 2,
self::Alert => 1,
self::Emergency => 0,
};
}
public const VALUES = [
100,
200,
250,
300,
400,
500,
550,
600,
];
public const NAMES = [
'DEBUG',
'INFO',
'NOTICE',
'WARNING',
'ERROR',
'CRITICAL',
'ALERT',
'EMERGENCY',
];
}

Some files were not shown because too many files have changed in this diff Show more