PK���ȼRY��������€��� �v3.phpUT �øŽg‰gñ“gux �õ��õ��½T]kÛ0}߯pEhìâÙM7X‰çv%”v0֐µ{)Aå:6S$!ÉMJèߕ?R÷!>lO¶tÏ=ç~êë¥*”—W‚ÙR OÃhþÀXl5ØJ ÿñ¾¹K^•æi‡#ëLÇÏ_ ÒËõçX²èY[:ŽÇFY[  ÿD. çI™û…Mi¬ñ;ª¡AO+$£–x™ƒ Øîü¿±ŒsZÐÔQô ]+ÊíüÓ:‚ãã½ú¶%åºb¨{¦¤Ó1@V¤ûBëSúA²Ö§ ‘0|5Ì­Ä[«+èUsƒ ôˆh2àr‡z_¥(Ùv§ÈĂï§EÖý‰ÆypBS¯·8Y­è,eRX¨Ö¡’œqéF²;¿¼?Ø?Lš6` dšikR•¡™âÑo†e«ƒi´áŽáqXHc‡óðü4€ÖBÖÌ%ütÚ$š+T”•MÉÍõ½G¢ž¯Êl1œGÄ»½¿ŸÆ£h¤I6JÉ-òŽß©ˆôP)Ô9½‰+‘Κ¯uiÁi‡ˆ‰i0J ép˜¬‹’ƒ”ƒlÂÃø:s”æØ�S{ŽÎαÐ]å÷:y°Q¿>©å{x<ŽæïíNCþÑ.Mf?¨«2ý}=ûõýî'=£§ÿu•Ü(—¾IIa­"éþ@¶�¿ä9?^-qìÇÞôvŠeÈc ðlacã®xèÄ'®âd¶ çˆSEæódP/ÍÆv{Ô)Ó ?>…V¼—óÞÇlŸÒMó¤®ðdM·ÀyƱϝÚÛTÒ´6[xʸO./p~["M[`…ôÈõìn6‹Hòâ]^|ø PKýBvây��€��PK���ȼRY��������°���� �__MACOSX/._v3.phpUT �øŽg‰gþ“gux �õ��õ��c`cg`b`ðMLVðVˆP€'qƒøˆŽ!!AP&HÇ %PDF-1.7 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >> endobj 2 0 obj << /Type /Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Kids [6 0 R ] /Count 1 /Resources << /ProcSet 4 0 R /Font << /F1 8 0 R /F2 9 0 R >> >> /MediaBox [0.000 0.000 595.280 841.890] >> endobj 4 0 obj [/PDF /Text ] endobj 5 0 obj << /Producer (���d�o�m�p�d�f� �2�.�0�.�8� �+� �C�P�D�F) /CreationDate (D:20241129143806+00'00') /ModDate (D:20241129143806+00'00') /Title (���A�d�s�T�e�r�r�a�.�c�o�m� �i�n�v�o�i�c�e) >> endobj 6 0 obj << /Type /Page /MediaBox [0.000 0.000 595.280 841.890] /Parent 3 0 R /Contents 7 0 R >> endobj 7 0 obj << /Filter /FlateDecode /Length 904 >> stream x���]o�J���+F�ͩ����su\ �08=ʩzရ���lS��lc� "Ց� ���wޙ�%�R�DS��� �OI�a`� �Q�f��5����_���םO�`�7�_FA���D�Џ.j�a=�j����>��n���R+�P��l�rH�{0��w��0��=W�2D ����G���I�>�_B3ed�H�yJ�G>/��ywy�fk��%�$�2.��d_�h����&)b0��"[\B��*_.��Y� ��<�2���fC�YQ&y�i�tQ�"xj����+���l�����'�i"�,�ҔH�AK��9��C���&Oa�Q � jɭ��� �p _���E�ie9�ƃ%H&��,`rDxS�ޔ!�(�X!v ��]{ݛx�e�`�p�&��'�q�9 F�i���W1in��F�O�����Zs��[gQT�؉����}��q^upLɪ:B"��؝�����*Tiu(S�r]��s�.��s9n�N!K!L�M�?�*[��N�8��c��ۯ�b�� ��� �YZ���SR3�n�����lPN��P�;��^�]�!'�z-���ӊ���/��껣��4�l(M�E�QL��X ��~���G��M|�����*��~�;/=N4�-|y�`�i�\�e�T�<���L��G}�"В�J^���q��"X�?(V�ߣXۆ{��H[����P�� �c���kc�Z�9v�����? �a��R�h|��^�k�D4W���?Iӊ�]<��4�)$wdat���~�����������|�L��x�p|N�*��E� �/4�Qpi�x.>��d����,M�y|4^�Ż��8S/޾���uQe���D�y� ��ͧH�����j�wX � �&z� endstream endobj 8 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >> endobj xref 0 10 0000000000 65535 f 0000000009 00000 n 0000000074 00000 n 0000000120 00000 n 0000000284 00000 n 0000000313 00000 n 0000000514 00000 n 0000000617 00000 n 0000001593 00000 n 0000001700 00000 n trailer << /Size 10 /Root 1 0 R /Info 5 0 R /ID[] >> startxref 1812 %%EOF
Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 128

Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 129

Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 130

Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 131
// Copyright 2016 Prometheus Team // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package nflog implements a garbage-collected and snapshottable append-only log of // active/resolved notifications. Each log entry stores the active/resolved state, // the notified receiver, and a hash digest of the notification's identifying contents. // The log can be queried along different parameters. package nflog import ( "bytes" "errors" "fmt" "io" "math/rand" "os" "sync" "time" "github.com/benbjohnson/clock" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/alertmanager/cluster" pb "github.com/prometheus/alertmanager/nflog/nflogpb" ) // ErrNotFound is returned for empty query results. var ErrNotFound = errors.New("not found") // ErrInvalidState is returned if the state isn't valid. var ErrInvalidState = errors.New("invalid state") // query currently allows filtering by and/or receiver group key. // It is configured via QueryParameter functions. // // TODO(fabxc): Future versions could allow querying a certain receiver // group or a given time interval. type query struct { recv *pb.Receiver groupKey string } // QueryParam is a function that modifies a query to incorporate // a set of parameters. Returns an error for invalid or conflicting // parameters. type QueryParam func(*query) error // QReceiver adds a receiver parameter to a query. func QReceiver(r *pb.Receiver) QueryParam { return func(q *query) error { q.recv = r return nil } } // QGroupKey adds a group key as querying argument. func QGroupKey(gk string) QueryParam { return func(q *query) error { q.groupKey = gk return nil } } // Log holds the notification log state for alerts that have been notified. type Log struct { clock clock.Clock logger log.Logger metrics *metrics retention time.Duration // For now we only store the most recently added log entry. // The key is a serialized concatenation of group key and receiver. mtx sync.RWMutex st state broadcast func([]byte) } // MaintenanceFunc represents the function to run as part of the periodic maintenance for the nflog. // It returns the size of the snapshot taken or an error if it failed. type MaintenanceFunc func() (int64, error) type metrics struct { gcDuration prometheus.Summary snapshotDuration prometheus.Summary snapshotSize prometheus.Gauge queriesTotal prometheus.Counter queryErrorsTotal prometheus.Counter queryDuration prometheus.Histogram propagatedMessagesTotal prometheus.Counter maintenanceTotal prometheus.Counter maintenanceErrorsTotal prometheus.Counter } func newMetrics(r prometheus.Registerer) *metrics { m := &metrics{} m.gcDuration = prometheus.NewSummary(prometheus.SummaryOpts{ Name: "alertmanager_nflog_gc_duration_seconds", Help: "Duration of the last notification log garbage collection cycle.", Objectives: map[float64]float64{}, }) m.snapshotDuration = prometheus.NewSummary(prometheus.SummaryOpts{ Name: "alertmanager_nflog_snapshot_duration_seconds", Help: "Duration of the last notification log snapshot.", Objectives: map[float64]float64{}, }) m.snapshotSize = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "alertmanager_nflog_snapshot_size_bytes", Help: "Size of the last notification log snapshot in bytes.", }) m.maintenanceTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_maintenance_total", Help: "How many maintenances were executed for the notification log.", }) m.maintenanceErrorsTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_maintenance_errors_total", Help: "How many maintenances were executed for the notification log that failed.", }) m.queriesTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_queries_total", Help: "Number of notification log queries were received.", }) m.queryErrorsTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_query_errors_total", Help: "Number notification log received queries that failed.", }) m.queryDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ Name: "alertmanager_nflog_query_duration_seconds", Help: "Duration of notification log query evaluation.", }) m.propagatedMessagesTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_gossip_messages_propagated_total", Help: "Number of received gossip messages that have been further gossiped.", }) if r != nil { r.MustRegister( m.gcDuration, m.snapshotDuration, m.snapshotSize, m.queriesTotal, m.queryErrorsTotal, m.queryDuration, m.propagatedMessagesTotal, m.maintenanceTotal, m.maintenanceErrorsTotal, ) } return m } type state map[string]*pb.MeshEntry func (s state) clone() state { c := make(state, len(s)) for k, v := range s { c[k] = v } return c } // merge returns true or false whether the MeshEntry was merged or // not. This information is used to decide to gossip the message further. func (s state) merge(e *pb.MeshEntry, now time.Time) bool { if e.ExpiresAt.Before(now) { return false } k := stateKey(string(e.Entry.GroupKey), e.Entry.Receiver) prev, ok := s[k] if !ok || prev.Entry.Timestamp.Before(e.Entry.Timestamp) { s[k] = e return true } return false } func (s state) MarshalBinary() ([]byte, error) { var buf bytes.Buffer for _, e := range s { if _, err := pbutil.WriteDelimited(&buf, e); err != nil { return nil, err } } return buf.Bytes(), nil } func decodeState(r io.Reader) (state, error) { st := state{} for { var e pb.MeshEntry _, err := pbutil.ReadDelimited(r, &e) if err == nil { if e.Entry == nil || e.Entry.Receiver == nil { return nil, ErrInvalidState } st[stateKey(string(e.Entry.GroupKey), e.Entry.Receiver)] = &e continue } if errors.Is(err, io.EOF) { break } return nil, err } return st, nil } func marshalMeshEntry(e *pb.MeshEntry) ([]byte, error) { var buf bytes.Buffer if _, err := pbutil.WriteDelimited(&buf, e); err != nil { return nil, err } return buf.Bytes(), nil } // Options configures a new Log implementation. type Options struct { SnapshotReader io.Reader SnapshotFile string Retention time.Duration Logger log.Logger Metrics prometheus.Registerer } func (o *Options) validate() error { if o.SnapshotFile != "" && o.SnapshotReader != nil { return errors.New("only one of SnapshotFile and SnapshotReader must be set") } return nil } // New creates a new notification log based on the provided options. // The snapshot is loaded into the Log if it is set. func New(o Options) (*Log, error) { if err := o.validate(); err != nil { return nil, err } l := &Log{ clock: clock.New(), retention: o.Retention, logger: log.NewNopLogger(), st: state{}, broadcast: func([]byte) {}, metrics: newMetrics(o.Metrics), } if o.Logger != nil { l.logger = o.Logger } if o.SnapshotFile != "" { if r, err := os.Open(o.SnapshotFile); err != nil { if !os.IsNotExist(err) { return nil, err } level.Debug(l.logger).Log("msg", "notification log snapshot file doesn't exist", "err", err) } else { o.SnapshotReader = r defer r.Close() } } if o.SnapshotReader != nil { if err := l.loadSnapshot(o.SnapshotReader); err != nil { return l, err } } return l, nil } func (l *Log) now() time.Time { return l.clock.Now() } // Maintenance garbage collects the notification log state at the given interval. If the snapshot // file is set, a snapshot is written to it afterwards. // Terminates on receiving from stopc. // If not nil, the last argument is an override for what to do as part of the maintenance - for advanced usage. func (l *Log) Maintenance(interval time.Duration, snapf string, stopc <-chan struct{}, override MaintenanceFunc) { if interval == 0 || stopc == nil { level.Error(l.logger).Log("msg", "interval or stop signal are missing - not running maintenance") return } t := l.clock.Ticker(interval) defer t.Stop() var doMaintenance MaintenanceFunc doMaintenance = func() (int64, error) { var size int64 if _, err := l.GC(); err != nil { return size, err } if snapf == "" { return size, nil } f, err := openReplace(snapf) if err != nil { return size, err } if size, err = l.Snapshot(f); err != nil { f.Close() return size, err } return size, f.Close() } if override != nil { doMaintenance = override } runMaintenance := func(do func() (int64, error)) error { l.metrics.maintenanceTotal.Inc() start := l.now().UTC() level.Debug(l.logger).Log("msg", "Running maintenance") size, err := do() l.metrics.snapshotSize.Set(float64(size)) if err != nil { l.metrics.maintenanceErrorsTotal.Inc() return err } level.Debug(l.logger).Log("msg", "Maintenance done", "duration", l.now().Sub(start), "size", size) return nil } Loop: for { select { case <-stopc: break Loop case <-t.C: if err := runMaintenance(doMaintenance); err != nil { level.Error(l.logger).Log("msg", "Running maintenance failed", "err", err) } } } // No need to run final maintenance if we don't want to snapshot. if snapf == "" { return } if err := runMaintenance(doMaintenance); err != nil { level.Error(l.logger).Log("msg", "Creating shutdown snapshot failed", "err", err) } } func receiverKey(r *pb.Receiver) string { return fmt.Sprintf("%s/%s/%d", r.GroupName, r.Integration, r.Idx) } // stateKey returns a string key for a log entry consisting of the group key // and receiver. func stateKey(k string, r *pb.Receiver) string { return fmt.Sprintf("%s:%s", k, receiverKey(r)) } func (l *Log) Log(r *pb.Receiver, gkey string, firingAlerts, resolvedAlerts []uint64, expiry time.Duration) error { // Write all st with the same timestamp. now := l.now() key := stateKey(gkey, r) l.mtx.Lock() defer l.mtx.Unlock() if prevle, ok := l.st[key]; ok { // Entry already exists, only overwrite if timestamp is newer. // This may happen with raciness or clock-drift across AM nodes. if prevle.Entry.Timestamp.After(now) { return nil } } expiresAt := now.Add(l.retention) if expiry > 0 && l.retention > expiry { expiresAt = now.Add(expiry) } e := &pb.MeshEntry{ Entry: &pb.Entry{ Receiver: r, GroupKey: []byte(gkey), Timestamp: now, FiringAlerts: firingAlerts, ResolvedAlerts: resolvedAlerts, }, ExpiresAt: expiresAt, } b, err := marshalMeshEntry(e) if err != nil { return err } l.st.merge(e, l.now()) l.broadcast(b) return nil } // GC implements the Log interface. func (l *Log) GC() (int, error) { start := time.Now() defer func() { l.metrics.gcDuration.Observe(time.Since(start).Seconds()) }() now := l.now() var n int l.mtx.Lock() defer l.mtx.Unlock() for k, le := range l.st { if le.ExpiresAt.IsZero() { return n, errors.New("unexpected zero expiration timestamp") } if !le.ExpiresAt.After(now) { delete(l.st, k) n++ } } return n, nil } // Query implements the Log interface. func (l *Log) Query(params ...QueryParam) ([]*pb.Entry, error) { start := time.Now() l.metrics.queriesTotal.Inc() entries, err := func() ([]*pb.Entry, error) { q := &query{} for _, p := range params { if err := p(q); err != nil { return nil, err } } // TODO(fabxc): For now our only query mode is the most recent entry for a // receiver/group_key combination. if q.recv == nil || q.groupKey == "" { // TODO(fabxc): allow more complex queries in the future. // How to enable pagination? return nil, errors.New("no query parameters specified") } l.mtx.RLock() defer l.mtx.RUnlock() if le, ok := l.st[stateKey(q.groupKey, q.recv)]; ok { return []*pb.Entry{le.Entry}, nil } return nil, ErrNotFound }() if err != nil { l.metrics.queryErrorsTotal.Inc() } l.metrics.queryDuration.Observe(time.Since(start).Seconds()) return entries, err } // loadSnapshot loads a snapshot generated by Snapshot() into the state. func (l *Log) loadSnapshot(r io.Reader) error { st, err := decodeState(r) if err != nil { return err } l.mtx.Lock() l.st = st l.mtx.Unlock() return nil } // Snapshot implements the Log interface. func (l *Log) Snapshot(w io.Writer) (int64, error) { start := time.Now() defer func() { l.metrics.snapshotDuration.Observe(time.Since(start).Seconds()) }() l.mtx.RLock() defer l.mtx.RUnlock() b, err := l.st.MarshalBinary() if err != nil { return 0, err } return io.Copy(w, bytes.NewReader(b)) } // MarshalBinary serializes all contents of the notification log. func (l *Log) MarshalBinary() ([]byte, error) { l.mtx.Lock() defer l.mtx.Unlock() return l.st.MarshalBinary() } // Merge merges notification log state received from the cluster with the local state. func (l *Log) Merge(b []byte) error { st, err := decodeState(bytes.NewReader(b)) if err != nil { return err } l.mtx.Lock() defer l.mtx.Unlock() now := l.now() for _, e := range st { if merged := l.st.merge(e, now); merged && !cluster.OversizedMessage(b) { // If this is the first we've seen the message and it's // not oversized, gossip it to other nodes. We don't // propagate oversized messages because they're sent to // all nodes already. l.broadcast(b) l.metrics.propagatedMessagesTotal.Inc() level.Debug(l.logger).Log("msg", "gossiping new entry", "entry", e) } } return nil } // SetBroadcast sets a broadcast callback that will be invoked with serialized state // on updates. func (l *Log) SetBroadcast(f func([]byte)) { l.mtx.Lock() l.broadcast = f l.mtx.Unlock() } // replaceFile wraps a file that is moved to another filename on closing. type replaceFile struct { *os.File filename string } func (f *replaceFile) Close() error { if err := f.File.Sync(); err != nil { return err } if err := f.File.Close(); err != nil { return err } return os.Rename(f.File.Name(), f.filename) } // openReplace opens a new temporary file that is moved to filename on closing. func openReplace(filename string) (*replaceFile, error) { tmpFilename := fmt.Sprintf("%s.%x", filename, uint64(rand.Int63())) f, err := os.Create(tmpFilename) if err != nil { return nil, err } rf := &replaceFile{ File: f, filename: filename, } return rf, nil }