Zabbix 4.4 přichází s druhou generací agenta v jazyce Go

11. 10. 2019
Doba čtení: 10 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
Společnost Zabbix SIA vydala 7. října 2019 novou verzi monitorovacího systému Zabbix s označením 4.4. Ta přináší spoustu nových vlastností, které si podrobněji popíšeme v dnešním článku.

Monitorovací systém Zabbix není asi nutné dlouze představovat. Za dobu své existence si našel místo v mnoha IT odvětvích. Jeho dynamický vývoj došel k nové vývojové verzi 4.4.

V pondělí 7. října 2019 byl vydán Zabbix 4.4 ve formě zdrojových kódů a balíčků v oficiálním Zabbix repository. Na stránkách dokumentace je velmi podrobný popis všech nových funkcí. V repozitáři je nově k dispozici i sestavení pro RHEL 8, SLES 15, Raspbian 10, Debian 10.

Zabbix agent 2

Zabbix agent nové generace byl vyvinut pod názvem Zabbix agent 2 ( zabbix_agent2). Některé z cílů stanovených při vývoji nového agenta:

  • snížit počet TCP spojení
  • větší souběžná kontrola
  • snadná rozšiřitelnost pomocí pluginů
  • náhrada C Zabbix agenta (s tím, že bude zachována předchozí funkcionalita)

Zabbix Agent 2 je zapsán v jazyce Go (s opětovným použitím C kódu Zabbix agenta). Pro kompilaci Zabbix agenta 2 je vyžadováno nakonfigurované prostředí Go verze 1.12+. Zabbix agent 2 je k dispozici v předkompilovaných balíčcích Zabbix.


Zabbix 4.4

Zabbix agent druhé generace

Webhooks v notifikacích

V nové verzi jsou rozšířeny funkcionality notifikací. Webhooks je nový media type, který umožňuje napsat vlastní kód v JavaScriptu, čímž se značně rozšiřují možnosti notifikací.

Zatímco v předchozích verzích, bylo možné použít jen externí skripty, nyní lze veškerou logiku výstrah udržovat uvnitř Zabbixu, konkrétně pomocí nového typu média Webhook.

Webhook lze použít pro snadnou integraci notifikací Zabbixu se systémem helpdesku třetích stran, pomocí chat, messengere atd.

Podrobnosti v dokumentaci Webhooks.

Oficiální podpora TimescaleDB

Podpora TimescaleDB, byla poprvé přidaná experimentálně v Zabbix 4.2, nyní je již oficiální. Viz dokumentaci Migrace na TimescaleDB.


Zabbix 4.4

Zabbx + TimescaleDB

Podívejte se na blog Zabbix, kde je příspěvek pro rychlé porovnání výkonu s PostgreSQL.

Agregační funkce v grafech

Grafy umožňují zobrazit data jako např. sloupe, nová verze poskytuje možnost vytváření agregovaných dat v grafech.

Můžete tedy vybrat požadované období (pět minut, hodinu, den) a poté zobrazit agregovanou hodnotu pro toto období místo všech hodnot. Možnosti agregace jsou následující:

  • min
  • max
  • avg
  • count
  • sum
  • first (první zobrazená hodnota)
  • last (poslední zobrazená hodnota)

Nejzajímavějším využitím agregace dat je možnost vytvořit souběžné porovnání dat za určité období:


Zabbix 4.4

Agregovaný graf

Agregaci lze nakonfigurovat v nastavení sady dat v konfiguraci grafického widgetu.


Zabbix 4.4

Agregovaný graf nastavení

Podrobnosti v dokumentaci agregace ve grafech.

Zvýšil se limit položky Dependent item

Maximální počet povolených závislých položek (Dependent item) pro jednu hlavní položku byl zvýšen z 999 na 29999.

Kerberos autentizace

Pro autentizaci webů je nově možné použít Kerberos:

  • Web monitoring
  • HTTP items

Zabbix 4.4

Kerberos autentizace pro web kontroly

Živá / provozní data problémů

Nyní je možné konfigurovat způsoby zobrazování provozních dat pro aktuální problémy. Nejnovější hodnoty položek na rozdíl od hodnot položek v době problému.

Zobrazení provozních dat lze konfigurovat ve filtru Monitoring → Problems nebo v konfiguraci příslušného widgetu řídicího panelu výběrem jedné ze tří možností:


Zabbix 4.4

Live data v událostech (problems)

S názvem problému – provozní data jsou připojena k názvu problému a v závorkách.


Zabbix 4.4

Live data v událostech (problems)

Obsah provozních dat lze konfigurovat s každým triggerem, který má nyní nové pole provozních dat (Operational data). Toto pole přijímá libovolný řetězec s makry, především makro  {ITEM.LASTVALUE <1-9>}.


Zabbix 4.4

Nastavení live data v triggeru

Provozní data lze také zahrnout do notifikací pomocí nového makra  {EVENT.OPDATA}.

Monitoring databáze může vrátit více řádků / sloupců

Byla přidána nová položka monitoringu databáze:

db.odbc.get[unique_description,data_source_name]

Ve srovnání s položkou db.odbc.select[], která již byla k dispozici v předchozích verzích, je nová položka schopna vracet hodnoty z více řádků a sloupců formátovaných jako JSON.

Může být tedy použit jako hlavní položka shromažďující všechny požadované metriky v jednom systémovém volání. Předzpracování JSONPath lze použít v závislých položkách k extrahování jednotlivých hodnot z této hlavní položky.

Nová položka může být také použita pro vyhledávání LLD pomocí dotazů ODBC SQL.

Ve srovnání s položkou db.odbc.discovery[] z předchozích verzí tato položka nedefinuje nízkoúrovňová detekční makra ve vráceném JSON, nicméně tyto makra můžou být uživatelem definována podle potřeby pomocí vlastní funkce makra LLD s JSONPath, aby ukazoval na požadované hodnoty ve vráceném JSON.

Low-level discovery

Low-level discovery pro bloková zařízení

Pomocí nového vestavěného detekčního klíče nízkoúrovňového objevování blokových zařízení a jejich typů je nově podporováno: vfs.dev.discovery.

Objevení vrátí JSON s hodnotami dvou maker – {#DEVNAME} a {#DEVTYPE}, identifikující název blokového zařízení a typ.

Tato makra lze použít k vytváření prototypů položek pomocí položek agentů vfs.dev.read[] a vfs.dev.write[], agent položka vfs.dev.read[{#DEVNAME},sps].

Vice dokumentace LLD blokových zařizení

Low-level discovery pro služby SystemD

Nízkoúrovňové objevování služeb systemd (služby výchozím nastavení) je podporováno pomocí nového vestavěného detekčního klíče: systemd.unit.discovery. Tento klíč je podporován pouze u Zabbix agenta druhé generace.

Objevení vrátí JSON s hodnotami několika maker, identifikující různé vlastnosti systémové jednotky. Tato makra lze použít k vytváření prototypů položek pomocí nového klíče položky systemd.unit.info[], např.  systemd.unit.info["{#UNIT.NAME}", LoadState].

Viz dokumentace:

V prvogeneračním agentu byla obdobná funkcionalita systemd dostupná např. pomocí modulu zabbix-module-systemd.

JMX MBean discovery s ne-ASCII řetězci

Nyní existuje nová položka pro objevování JMX MBean, která negeneruje LLD makra, a proto může vracet hodnoty bez omezení souvisejících s generováním názvu makra LLD (například spojovníky, hranaté závorky a jiné znaky než ASCII): jmx.get[<discovery_mo­de>,<object_name>]

Pokud položka jmx.discovery[] z předchozích verzí narazila na JMX MBean, které nemohly být převedeny na název makra (kvůli nepodporovaným znakům v generování názvu makra LLD), musely tyto vlastnosti ignorovat.

Nová položka jmx.get[] negeneruje názvy maker LLD ve vráceném JSON. Namísto toho mohou být názvy maker LLD definovány na kartě uživatelských maker pravidla zjišťování pomocí JSONPath pro směřování na požadované hodnoty.

Podrobnosti v dokumentaci: Discovery JMX objects

WMI discovery

Byla přidána nová položka agenta systému Windows: wmi.getall[<names­pace>,<query>]

Ve srovnání s položkou wmi.get[], která již byla k dispozici v předchozích verzích, je nová položka schopna vrátit celou odpověď na dotaz, formátovanou jako JSON. Předzpracování JSONPath lze použít k označení konkrétnějších hodnot vráceného JSON.

Nová položka může být použita pro LLD pomocí dotazů WMI.

Přestože tato položka nedefinuje LLD makra ve vráceném JSON, uživatel může tato makra definovat podle potřeby pomocí vlastní funkce makra LLD s JSONPath, aby ukazoval na požadované hodnoty ve vráceném JSON.

Bezpečná auto-registrace

Dříve byla veškerá komunikace během autorizace registrace Zabbix agenta prováděna nešifrovaně. V nové verzi je bezpečný způsob autorizace možný pomocí konfigurace autentizace založené na PSK se šifrovaným připojením.

Úroveň šifrování je konfigurována globálně v sekci Administration → General, v nové části Autoregistration přístupné prostřednictvím rozbalovací nabídky vpravo. Je možné vybrat žádné šifrování, TLS šifrování s ověřováním PSK nebo obojí (takže někteří hostitelé se mohou registrovat bez šifrování, zatímco jiní pomocí šifrování):


Zabbix 4.4

Globální nastavení zabezpečení auto-registrace


Zabbix 4.4

Nastavení PSK auto-registrace

Autentizace pomocí PSK je realizována Zabbix serverem před přidáním hostitele. Pokud nastane úspěšné ověření, hostitel bude přidán.

Auto-registrace DNS jménem

Nyní je možné určit, že hostitel by měl být registrovan s názvem DNS jako výchozí rozhraní agenta. Za tímto účelem je třeba zadat / vrátit název DNS jako hodnotu konfiguračních parametrů „HostInterface“ nebo „HostInterfaceItem“.

Mějte na paměti, že pokud se změní hodnota jednoho ze dvou parametrů, aktualizuje se autorizované rozhraní hostitele. Je tedy možné aktualizovat výchozí rozhraní na jiné jméno DNS nebo IP adresu. Aby se však změny projevily, musí být agent restartován. Konfigurační parametry „HostInterface“ nebo „HostInterfaceItem“ jsou podporovány od Zabbix 4.4.

Při objevování jsou povoleny delší názvy hostů

Maximální povolená délka názvu hostitele se při objevování hostitele a automatické registraci aktivního agenta zvýšila ze 64 znaků na 128 znaků.

Rozšířené možnosti preprocessing

Vlastní zpracování chyb je nyní k dispozici také pro následující kroky předzpracování:

  • Check for error in JSON
  • Check for error in XML
  • Check for error using regular expression

V typickém případě vlastního zpracování chyb lze data přeskočit, pokud se objeví chybová zpráva.

Hostname zahrnutý do real-time exportu

Názvy hostitelů jsou nyní zahrnuty do exportu událostí, hodnoty položek a trendů v reálném čase (dříve byl exportován pouze viditelný název hostitele). Protokol exportu se změnil s informacemi o názvu hostitele. Nyní s objektem, nikoli string/array.

Nové šablony

K dispozici jsou nové oficiální šablony:

Linux

Nové monitorovací šablony systému Linux mají tři varianty:

  • Template OS Linux podle Zabbix agent, Template OS Linux podle Zabbix agent active – Linux monitorován pomocí Zabbix agent (modular) – závisí na Template Module Zabbix agent, což by mělo být import/update jako první
  • Template OS Linux podle Prom – Linux monitorován pomocí node exporter (monolithic)
  • Template OS Linux SNMPv2 – Linux monitorován pomocí SNMPv2 (modular) – závisí na Template Module Generic SNMPv2, což by mělo být import/update jako první – závisí na Template Module Interfaces SNMPv2, což by mělo být import/update jako první

Windows

Template OS Windows podle Zabbix agent, Template OS Windows podle Zabbix agent active – Windows monitorován pomocí Zabbix agent. Tyto šablony jsou podporovány Windows Server 2008/Vista a výše.

Cisco UCS server

Template Server Cisco UCS SNMPv2 – Cisco UCS server monitorovací šablona.

Nginx

Apache

  • Template App Apache podle Zabbix agent – shromažďuje metriky podle mod_status lokálne pomocí Zabbix agent (viz popis).
  • Template App Apache podle HTTP – shromažďuje metriky podle mod_status pomocí HTTP agent vzdáleně (viz popis).

RabbitMQ

  • Template App RabbitMQ cluster podle Zabbix agent a Template App RabbitMQ node pomocí Zabbix agent – shromažďuje metriky podle RabbitMQ management plugin pomocí Zabbix agent lokálně.
  • Template App RabbitMQ cluster podle HTTP a Template App RabbitMQ node pomocí HTTP – shromažďuje metriky podle RabbitMQ management plugin pomocí HTTP agent vzdáleně.

MySQL/MariaDB

Template DB MySQL – viz požadavky na provoz této šablony.

PostgreSQL

Template DB PostgreSQL – viz požadavky na provoz této šablony. Můžete získat tyto šablony:

V sekci Configuration → Templates v nových instalacích. Pokud upgradujete z předchozích verzí, můžete si tyto šablony stáhnout z úložiště Zabbix Git nebo je najít v adresáři šablon stažené nejnovější verze Zabbix. Poté je můžete v sekci Configuration → Templates importovat ručně do Zabbixu.

Pokud již existují šablony se stejnými názvy, je třeba při importu zaškrtnout možnost Odstranit chybějící, aby bylo dosaženo čistého importu. Tímto způsobem budou odstraněny staré položky, které již nejsou v aktualizované šabloně (pamatujte, že to bude znamenat ztrátu historie těchto starých položek).

Jabber, Ez Textové media type byly odstraněny

Typy médií Jabber a Ez Texting pro doručování oznámení byly odstraněny.

Pokud jsou tyto typy médií přítomny ve vaší existující instalaci, budou během upgradu nahrazeny typem skriptového média se zachovanými všemi relevantními parametry. Oznámení přes Jabber a Ez Texting však již nebudou fungovat.

Frontend

Default dashboard

Výchozí řídicí panel dodávaný s novými instalacemi (Global view) byl vylepšen. U aktualizovaného widgetu Problems podle závažnosti byl přidán souhrnný pohled na problémy. Byl přidán nový widget dostupnosti hostitele. Několik widgetů má nyní skryté záhlaví včetně widgetu Hodiny.


Zabbix 4.4

Default dashboard

V rámci Dashboardu v novém Zabbix 4.4 došlo k mnoha změnám a vylepšení, které jsou souhrnně popsány.

Export/import

Media types

Typy médií lze nyní pro snadnější sdílení exportovat a importovat.

Byl změněn formát exportu hostitele

Formát exportu hostitele a šablony v XML/JSON byl změněn ve způsobu exportu triggerů. Dříve byly všechny triggery uvedeny za informacemi o hostiteli. Nyní, pro dosažení lepší čitelnosti, jsou spouštěče, které jsou založeny na jedné hostitelské položce pouze v problémech a výrazu obnovy, uvedeny ve značkách příslušné hostitelské položky.

bitcoin_smenarna

Makra

{EVENT.ID} je nyní podporována ve spouštěcích adresách URL, což umožňuje vytvářet úplné adresy URL k podrobnostem o problému.

Výkon

Databazová tabulka Items byla dříve používána frontendem i serverem, což vedlo k nežádoucímu zamykání řádků v době, kdy např. server aktualizoval pole ‚log‘ items. K vyřešení této situace bylo pole v reálném čase (lastlogsize, state, mtime, error) rozdělena do samostatné tabulky nazvané  item_rtdata.

Autor článku

Pracuje jako systém administrátor – UNIX/Linux systémů. Vystudoval VOŠ Liberec, obor Počítačové systémy. Ve svém oboru prosazuje otevřená řešení IT infrastruktury.

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »