diff --git a/CHANGELOG.md b/CHANGELOG.md index 741a64d..fffc52b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog Newest updates are at the top of this file. +## Sep 10 2020 - v5.1.2 +* mqmetric - Add loglevel=TRACE and trace-points for all key functions in the package +* mqmetric - Add channel status bytes and buffer counts +* mqmetric - Check queue depth appropriate for all CHSTATUS operations +* ibmmq - Fix for potential race condition (#148) + ## Aug 07 2020 - v5.1.1 * ibmmq - Fix STS structure (#146) * Add flag for Windows build that seems no longer to be automatically set by cgo diff --git a/Dockerfile b/Dockerfile index a0f11bc..f1d4cc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,11 +55,17 @@ ENV RDURL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messag VRMF=9.2.0.0 # Install the MQ client from the Redistributable package. This also contains the -# header files we need to compile against. +# header files we need to compile against. Setup the subset of the package +# we are going to keep - the genmqpkg.sh script removes unneeded parts +ENV genmqpkg_incnls=1 \ + genmqpkg_incsdk=1 \ + genmqpkg_inctls=1 + RUN cd /opt/mqm \ && curl -LO "$RDURL/$VRMF-$RDTAR" \ && tar -zxf ./*.tar.gz \ - && rm -f ./*.tar.gz + && rm -f ./*.tar.gz \ + && bin/genmqpkg.sh -b /opt/mqm # Insert the script that will do the build COPY buildInDocker.sh $GOPATH diff --git a/ibmmq/mqicb.go b/ibmmq/mqicb.go index 7bc060a..3e8e92f 100644 --- a/ibmmq/mqicb.go +++ b/ibmmq/mqicb.go @@ -35,6 +35,7 @@ import "C" import ( "fmt" "strings" + "sync" "unsafe" ) @@ -54,6 +55,9 @@ type cbInfo struct { // This map is indexed by a combination of the hConn and hObj values var cbMap = make(map[string]*cbInfo) +// Add a mutex to control access to it as there may be several threads going for different qmgrs +var mutex sync.Mutex + /* MQCALLBACK_Go is a wrapper callback function that will invoke the user-supplied callback after converting the C structures into the corresponding Go format. @@ -90,7 +94,9 @@ func MQCALLBACK_Go(hConn C.MQHCONN, mqmd *C.MQMD, mqgmo *C.MQGMO, mqBuffer C.PMQ } key := makeKey(hConn, mqcbc.Hobj) + mapLock() info, ok := cbMap[key] + mapUnlock() // The MQ Client libraries seem to sometimes call us with an EVENT // even if it's not been registered. And therefore the cbMap does not @@ -105,6 +111,7 @@ func MQCALLBACK_Go(hConn C.MQHCONN, mqmd *C.MQMD, mqgmo *C.MQGMO, mqBuffer C.PMQ if !ok { if gocbc.CallType == MQCBCT_EVENT_CALL && mqcbc.Hobj == C.MQHO_NONE { key = makePartialKey(hConn) + mapLock() for k, i := range cbMap { if strings.HasPrefix(k, key) { ok = true @@ -114,6 +121,7 @@ func MQCALLBACK_Go(hConn C.MQHCONN, mqmd *C.MQMD, mqgmo *C.MQGMO, mqBuffer C.PMQ break } } + mapUnlock() } } else { cbHObj = info.hObj @@ -185,14 +193,18 @@ func (object *MQObject) CB(goOperation int32, gocbd *MQCBD, gomd *MQMD, gogmo *M // Add or remove the control information in the map used by the callback routines switch mqOperation { case C.MQOP_DEREGISTER: + mapLock() delete(cbMap, key) + mapUnlock() case C.MQOP_REGISTER: // Stash the hObj and real function to be called info := &cbInfo{hObj: object, callbackFunction: gocbd.CallbackFunction, connectionArea: nil, callbackArea: gocbd.CallbackArea} + mapLock() cbMap[key] = info + mapUnlock() default: // Other values leave the map alone } @@ -230,14 +242,18 @@ func (object *MQQueueManager) CB(goOperation int32, gocbd *MQCBD) error { // Add or remove the control information in the map used by the callback routines switch mqOperation { case C.MQOP_DEREGISTER: + mapLock() delete(cbMap, key) + mapUnlock() case C.MQOP_REGISTER: // Stash an hObj and real function to be called info := &cbInfo{hObj: &MQObject{qMgr: object, Name: ""}, callbackFunction: gocbd.CallbackFunction, connectionArea: nil, callbackArea: gocbd.CallbackArea} + mapLock() cbMap[key] = info + mapUnlock() default: // Other values leave the map alone } @@ -259,11 +275,13 @@ func (x *MQQueueManager) Ctl(goOperation int32, goctlo *MQCTLO) error { // Need to make sure control information is available before the callback // is enabled. So this gets setup even if the MQCTL fails. key := makePartialKey(x.hConn) + mapLock() for k, info := range cbMap { if strings.HasPrefix(k, key) { info.connectionArea = goctlo.ConnectionArea } } + mapUnlock() C.MQCTL(x.hConn, mqOperation, (C.PMQVOID)(unsafe.Pointer(&mqctlo)), &mqcc, &mqrc) @@ -295,14 +313,25 @@ func makePartialKey(hConn C.MQHCONN) string { func cbRemoveConnection(hConn C.MQHCONN) { // Remove all of the hObj values for this hconn key := makePartialKey(hConn) + mapLock() for k, _ := range cbMap { if strings.HasPrefix(k, key) { delete(cbMap, k) } } + mapUnlock() } func cbRemoveHandle(hConn C.MQHCONN, hObj C.MQHOBJ) { key := makeKey(hConn, hObj) + mapLock() delete(cbMap, key) + mapUnlock() +} + +func mapLock() { + mutex.Lock() +} +func mapUnlock() { + mutex.Unlock() } diff --git a/ibmmq/mqistr.go b/ibmmq/mqistr.go index 4d6f8dd..1f1f93d 100644 --- a/ibmmq/mqistr.go +++ b/ibmmq/mqistr.go @@ -1,5 +1,3 @@ -// +build go1.13 - package ibmmq /* diff --git a/mqmetric/channel.go b/mqmetric/channel.go index 7cbae4d..3bc1ded 100644 --- a/mqmetric/channel.go +++ b/mqmetric/channel.go @@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB. package mqmetric /* - Copyright (c) IBM Corporation 2016, 2019 + Copyright (c) IBM Corporation 2016, 2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -43,6 +43,10 @@ const ( ATTR_CHL_RQMNAME = "rqmname" ATTR_CHL_MESSAGES = "messages" + ATTR_CHL_BYTES_SENT = "bytes_sent" + ATTR_CHL_BYTES_RCVD = "bytes_rcvd" + ATTR_CHL_BUFFERS_SENT = "buffers_sent" + ATTR_CHL_BUFFERS_RCVD = "buffers_rcvd" ATTR_CHL_BATCHES = "batches" ATTR_CHL_STATUS = "status" ATTR_CHL_STATUS_SQUASH = ATTR_CHL_STATUS + "_squash" @@ -81,7 +85,9 @@ text. The elements can be expanded later; just trying to give a starting point for now. */ func ChannelInitAttributes() { + traceEntry("ChannelInitAttributes") if chlAttrsInit { + traceExit("ChannelInitAttributes", 1) return } ChannelStatus.Attributes = make(map[string]*StatusAttribute) @@ -101,6 +107,18 @@ func ChannelInitAttributes() { attr = ATTR_CHL_MESSAGES ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Messages (API Calls for SVRCONN)", ibmmq.MQIACH_MSGS) ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values + attr = ATTR_CHL_BYTES_SENT + ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Bytes sent", ibmmq.MQIACH_BYTES_SENT) + ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values + attr = ATTR_CHL_BYTES_RCVD + ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Bytes rcvd", ibmmq.MQIACH_BYTES_RCVD) + ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values + attr = ATTR_CHL_BUFFERS_SENT + ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Buffers sent", ibmmq.MQIACH_BUFFERS_SENT) + ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values + attr = ATTR_CHL_BUFFERS_RCVD + ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Buffers rcvd", ibmmq.MQIACH_BUFFERS_RCVD) + ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values attr = ATTR_CHL_BATCHES ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Completed Batches", ibmmq.MQIACH_BATCHES) ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values @@ -156,18 +174,26 @@ func ChannelInitAttributes() { attr = ATTR_CHL_MAX_INSTC ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "MaxInstC", -1) + traceExit("ChannelInitAttributes", 0) } // If we need to list the channels that match a pattern. Not needed for // the status queries as they (unlike the pub/sub resource stats) accept // patterns in the PCF command func InquireChannels(patterns string) ([]string, error) { + traceEntry("InquireChannels") ChannelInitAttributes() - return inquireObjects(patterns, ibmmq.MQOT_CHANNEL) + rc, err := inquireObjects(patterns, ibmmq.MQOT_CHANNEL) + + traceExitErr("InquireChannels", 0, err) + return rc, err } func CollectChannelStatus(patterns string) error { var err error + + traceEntry("CollectChannelStatus") + channelsSeen = make(map[string]bool) // Record which channels have been seen in this period ChannelInitAttributes() @@ -178,6 +204,7 @@ func CollectChannelStatus(patterns string) error { channelPatterns := strings.Split(patterns, ",") if len(channelPatterns) == 0 { + traceExit("CollectChannelStatus", 1) return nil } @@ -245,6 +272,7 @@ func CollectChannelStatus(patterns string) error { } } } + traceExitErr("CollectChannelStatus", 0, err) return err } @@ -253,6 +281,9 @@ func CollectChannelStatus(patterns string) error { // Collect the responses and build up the statistics func collectChannelStatus(pattern string, instanceType int32) error { var err error + + traceEntryF("collectChannelStatus", "Pattern: %s", pattern) + statusClearReplyQ() putmqmd, pmo, cfh, buf := statusSetCommandHeaders() @@ -283,6 +314,7 @@ func collectChannelStatus(pattern string, instanceType int32) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("collectChannelStatus", 1, err) return err } @@ -298,6 +330,7 @@ func collectChannelStatus(pattern string, instanceType int32) error { } } + traceExitErr("collectChannelStatus", 0, err) return err } @@ -305,6 +338,8 @@ func collectChannelStatus(pattern string, instanceType int32) error { func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter + traceEntry("parseChlData") + chlName := "" connName := "" jobName := "" @@ -321,6 +356,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseChlData", 1) return "" } @@ -379,6 +415,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { for k, _ := range channelsSeen { re := regexp.MustCompile(subKey) if re.MatchString(k) { + traceExit("parseChlData", 2) return "" } } @@ -421,6 +458,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { ChannelStatus.Attributes[ATTR_CHL_MAX_INST].Values[key] = newStatusValueInt64(maxInst) } + traceExitF("parseChlData", 0, "Key: %s", key) return key } @@ -430,6 +468,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { func ChannelNormalise(attr *StatusAttribute, v int64) float64 { var f float64 + traceEntry("ChannelNormalise") if attr.squash { switch attr.pcfAttr { @@ -467,6 +506,9 @@ func ChannelNormalise(attr *StatusAttribute, v int64) float64 { f = 0 } } + + traceExit("ChannelNormalise", 0) + return f } @@ -476,9 +518,12 @@ func ChannelNormalise(attr *StatusAttribute, v int64) float64 { func inquireChannelAttributes(objectPatternsList string, infoMap map[string]*ObjInfo) error { var err error + traceEntry("inquireChannelAttributes") + statusClearReplyQ() if objectPatternsList == "" { + traceExitErr("inquireChannelAttributes", 1, err) return err } @@ -528,6 +573,7 @@ func inquireChannelAttributes(objectPatternsList string, infoMap map[string]*Obj // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("inquireChannelAttributes", 2, err) return err } @@ -538,6 +584,8 @@ func inquireChannelAttributes(objectPatternsList string, infoMap map[string]*Obj } } } + traceExit("inquireChannelAttributes", 0) + return nil } @@ -546,6 +594,8 @@ func parseChannelAttrData(cfh *ibmmq.MQCFH, buf []byte, infoMap map[string]*ObjI var ci *ObjInfo var ok bool + traceEntry("parseChannelAttrData") + chlName := "" parmAvail := true @@ -553,6 +603,7 @@ func parseChannelAttrData(cfh *ibmmq.MQCFH, buf []byte, infoMap map[string]*ObjI offset := 0 datalen := len(buf) if cfh.ParameterCount == 0 { + traceExit("parseChannelAttrData", 1) return } // Parse it once to extract the fields that are needed for the map key @@ -631,6 +682,7 @@ func parseChannelAttrData(cfh *ibmmq.MQCFH, buf []byte, infoMap map[string]*ObjI } } + traceExit("parseChannelAttrData", 0) return } diff --git a/mqmetric/discover.go b/mqmetric/discover.go index 5c64a45..c18e6f3 100644 --- a/mqmetric/discover.go +++ b/mqmetric/discover.go @@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB. package mqmetric /* - Copyright (c) IBM Corporation 2016, 2019 + Copyright (c) IBM Corporation 2016, 2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -82,7 +82,7 @@ type AllMetrics struct { // This structure contains additional info about any object type that may need to be held. // The first fields are used for all object types. Following fields will apply to different -// object types. Currently only queues need this information. +// object types. type ObjInfo struct { exists bool // Used during rediscovery firstCollection bool // To indicate discard needed of first stat @@ -120,10 +120,12 @@ var discoveryDone = false var publicationCount = 0 func GetDiscoveredQueues() []string { + traceEntry("GetDiscoveredQueues") keys := make([]string, 0) for key := range qInfoMap { keys = append(keys, key) } + traceExit("GetDiscoveredQueues", 0) return keys } @@ -149,6 +151,8 @@ func VerifyConfig() (int32, error) { var err error var v map[int32]interface{} var compCode = ibmmq.MQCC_OK + + traceEntry("VerifyConfig") if !discoveryDone { err = fmt.Errorf("Error: Need to call DiscoverAndSubscribe first") compCode = ibmmq.MQCC_FAILED @@ -169,7 +173,18 @@ func VerifyConfig() (int32, error) { // Since we don't do pubsub-based collection on z/OS, this qdepth doesn't matter recommendedDepth := (20 + len(qInfoMap)*5) * 6 if maxQDepth < int32(recommendedDepth) && usePublications { - err = fmt.Errorf("Warning: Maximum queue depth on %s may be too low. Current value = %d", replyQBaseName, maxQDepth) + err = fmt.Errorf("Warning: Maximum queue depth on %s may be too low. Current value = %d. Suggested depth based on queue count is at least %d", replyQBaseName, maxQDepth, recommendedDepth) + compCode = ibmmq.MQCC_WARNING + } + + // There may also be a high number of channels that meet the selection criteria. Make sure we've got enough space + // for a DIS CHS(*) in case that pattern is used. I've added a small bit of headroom but we will normally only get + // exactly the number of responses to match the number of actual channels. Of course, that number may change in the + // lifetime of the system but we only check what's possible at startup.If the channels are being named via a set of + // separate patterns, then this will overestimate what's needed. Hence it's a warning, not an error. + recommendedDepth = len(chlInfoMap) + 20 + if maxQDepth < int32(recommendedDepth) && len(chlInfoMap) > 0 { + err = fmt.Errorf("Warning: Maximum queue depth on %s may be too low. Current value = %d. Suggested depth based on channel count is at least %d\n", replyQBaseName, maxQDepth, recommendedDepth) compCode = ibmmq.MQCC_WARNING } @@ -183,6 +198,9 @@ func VerifyConfig() (int32, error) { } } } + + traceExitErr("VerifyConfig", 0, err) + return compCode, err } @@ -192,15 +210,21 @@ different resources available from a queue manager and issuing the MQSUB calls to collect the data */ func DiscoverAndSubscribe(dc DiscoverConfig) error { + traceEntry("DiscoverAndSubscribe") + discoveryDone = true redo := false qInfoMap = make(map[string]*ObjInfo) err := discoverAndSubscribe(dc, redo) + + traceExitErr("DiscoverAndSubscribe", 0, err) return err } func RediscoverAndSubscribe(dc DiscoverConfig) error { + traceEntry("RediscoverAndSubscribe") + discoveryDone = true redo := true @@ -219,6 +243,8 @@ func RediscoverAndSubscribe(dc DiscoverConfig) error { } } + traceExitErr("RediscoverAndSubscribe", 0, err) + return err } @@ -227,6 +253,7 @@ func RediscoverAttributes(objectType int32, objectPatterns string) error { var infoMap map[string](*ObjInfo) var fn func(string, map[string](*ObjInfo)) error + traceEntry("RediscoverAttributes") switch objectType { case ibmmq.MQOT_CHANNEL: // Always start with a clean slate for these maps @@ -246,12 +273,16 @@ func RediscoverAttributes(objectType int32, objectPatterns string) error { } } } + + traceExitErr("RediscoverAttributes", 0, err) + return err } func discoverAndSubscribe(dc DiscoverConfig, redo bool) error { var err error + traceEntry("discoverAndSubscribe") // What metrics can the queue manager provide? if err == nil && redo == false { err = discoverStats(dc) @@ -288,6 +319,8 @@ func discoverAndSubscribe(dc DiscoverConfig, redo bool) error { err = createSubscriptions() } + traceExitErr("discoverAndSubscribe", 0, err) + return err } @@ -298,6 +331,7 @@ func discoverClasses(dc DiscoverConfig, metaPrefix string) error { var err error var rootTopic string + traceEntry("discoverClasses") // Have to know the starting point for the topic that tells about classes if metaPrefix == "" { rootTopic = "$SYS/MQ/INFO/QMGR/" + resolvedQMgrName + "/Monitor/METADATA/CLASSES" @@ -335,7 +369,9 @@ func discoverClasses(dc DiscoverConfig, metaPrefix string) error { case ibmmq.MQCA_TOPIC_STRING: cl.typesTopic = elem.String[0] default: - return fmt.Errorf("Unknown parameter %d in class discovery", elem.Parameter) + e2 := fmt.Errorf("Unknown parameter %d in class discovery", elem.Parameter) + traceExitErr("discoverClasses", 1, e2) + return e2 } } if cl.Name != "STATAPP" { @@ -347,6 +383,7 @@ func discoverClasses(dc DiscoverConfig, metaPrefix string) error { } subsOpened = true + traceExitErr("discoverClasses", 0, err) return err } @@ -356,6 +393,8 @@ func discoverTypes(dc DiscoverConfig, cl *MonClass) error { var metaReplyQObj ibmmq.MQObject var err error + traceEntry("discoverTypes") + sub, err = subscribeManaged(cl.typesTopic, &metaReplyQObj) if err == nil { data, err = getMessageWithHObj(true, metaReplyQObj) @@ -389,7 +428,9 @@ func discoverTypes(dc DiscoverConfig, cl *MonClass) error { case ibmmq.MQCA_TOPIC_STRING: ty.elementTopic = elem.String[0] default: - return fmt.Errorf("Unknown parameter %d in type discovery", elem.Parameter) + e2 := fmt.Errorf("Unknown parameter %d in type discovery", elem.Parameter) + traceExitErr("discoverTypes", 1, e2) + return e2 } } if ty.Parent.Name == "STATQ" && dc.MonitoredQueues.SubscriptionSelector != "" { @@ -403,6 +444,8 @@ func discoverTypes(dc DiscoverConfig, cl *MonClass) error { } } } + + traceExitErr("discoverTypes", 0, err) return err } @@ -413,6 +456,7 @@ func discoverElements(dc DiscoverConfig, ty *MonType) error { var metaReplyQObj ibmmq.MQObject var elem *MonElement + traceEntry("discoverElements") sub, err = subscribeManaged(ty.elementTopic, &metaReplyQObj) if err == nil { data, err = getMessageWithHObj(true, metaReplyQObj) @@ -449,7 +493,9 @@ func discoverElements(dc DiscoverConfig, ty *MonType) error { case ibmmq.MQCAMO_MONITOR_DESC: elem.Description = e.String[0] default: - return fmt.Errorf("Unknown parameter %d in type discovery", e.Parameter) + e2 := fmt.Errorf("Unknown parameter %d in type discovery", e.Parameter) + traceExitErr("discoverElements", 1, e2) + return e2 } } @@ -458,6 +504,8 @@ func discoverElements(dc DiscoverConfig, ty *MonType) error { } } + traceExitErr("discoverElements", 0, err) + return err } @@ -470,7 +518,9 @@ func discoverElementsNLS(dc DiscoverConfig, ty *MonType, locale string) error { var sub ibmmq.MQObject var metaReplyQObj ibmmq.MQObject + traceEntry("discoverElementsNLS") if locale == "" { + traceExit("discoverElementsNLS", 1) return nil } @@ -522,6 +572,8 @@ func discoverElementsNLS(dc DiscoverConfig, ty *MonType, locale string) error { } } + traceExitErr("discoverElementsNLS", 0, err) + return err } @@ -534,12 +586,14 @@ Then discover the list of individual queues we have been asked for. func discoverStats(dc DiscoverConfig) error { var err error + traceEntry("discoverStats") metaPrefix := dc.MetaPrefix // Start with an empty set of information about the available stats Metrics.Classes = make(map[int]*MonClass) // Allow us to proceed on z/OS even though it does not support pub/sub resources if metaPrefix == "" && !usePublications { + traceExit("discoverStats", 1) return nil } @@ -583,6 +637,8 @@ func discoverStats(dc DiscoverConfig) error { } + traceExitErr("discoverStats", 0, err) + return err } @@ -605,6 +661,7 @@ func discoverQueues(monitoredQueuePatterns string) error { var allQueues []string usingRegExp := false + traceEntry("discoverQueues") // If the list of monitored queues has a ! somewhere in it, we will // get the full list of queues on the qmgr, and filter it by patterns. if strings.Contains(monitoredQueuePatterns, "!") { @@ -673,9 +730,13 @@ func discoverQueues(monitoredQueuePatterns string) error { if err != nil { //fmt.Printf("Queue Discovery Error: %v\n", err) } + traceExit("discoverQueues", 1) + return nil } + traceExitErr("discoverQueues", 0, err) + return err } @@ -689,9 +750,11 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro var returnedAttribute int32 var missingPatterns string + traceEntryF("inquireObjects", "Type: %d Patterns: %s", objectType, objectPatternsList) objectList = make([]string, 0) if objectPatternsList == "" { + traceExitErr("inquireObjects", 1, err) return nil, err } @@ -720,7 +783,9 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro attribute = ibmmq.MQCACH_CHANNEL_NAME returnedAttribute = ibmmq.MQCACH_CHANNEL_NAMES default: - return nil, fmt.Errorf("Object type %d is not valid", objectType) + e2 := fmt.Errorf("Object type %d is not valid", objectType) + traceExitErr("inquireObjects", 2, e2) + return nil, e2 } putmqmd := ibmmq.NewMQMD() @@ -778,6 +843,7 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("inquireObjects", 3, err) return objectList, err } @@ -829,9 +895,7 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro } for i := 0; i < len(elem.String); i++ { s := strings.TrimSpace(elem.String[i]) - objectList = append(objectList, s) - } } } @@ -845,6 +909,7 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro bufSize = maxBufSize } } else { + traceExitErr("inquireObjects", 4, err) return objectList, err } } @@ -854,6 +919,7 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro if len(missingPatterns) > 0 && err == nil { err = fmt.Errorf("No objects matching %s of type %d exist", missingPatterns, objectType) } + traceExitErr("inquireObjects", 0, err) return objectList, err } @@ -864,6 +930,8 @@ create all the subscriptions. func createSubscriptions() error { var err error var sub ibmmq.MQObject + + traceEntry("createSubscriptions") for _, cl := range Metrics.Classes { for _, ty := range cl.Types { @@ -905,11 +973,15 @@ func createSubscriptions() error { } if err != nil { - return fmt.Errorf("Error subscribing to %s: %v", ty.ObjectTopic, err) + e2 := fmt.Errorf("Error subscribing to %s: %v", ty.ObjectTopic, err) + traceExitErr("createSubscriptions", 1, e2) + return e2 } } } + traceExitErr("createSubscriptions", 0, err) + return err } @@ -934,9 +1006,12 @@ func ProcessPublications() error { var elementidx int var value int64 + traceEntry("ProcessPublications") + publicationCount = 0 if !usePublications { + traceExit("ProcessPublications", 1) return nil } @@ -1052,6 +1127,7 @@ func ProcessPublications() error { mqreturn := err.(*ibmmq.MQReturn) if mqreturn.MQCC == ibmmq.MQCC_FAILED && mqreturn.MQRC != ibmmq.MQRC_NO_MSG_AVAILABLE { + traceExitErr("ProcessPublications", 2, mqreturn) return mqreturn } } @@ -1062,6 +1138,7 @@ func ProcessPublications() error { qi.firstCollection = false } + traceExit("ProcessPublications", 0) return nil } @@ -1081,6 +1158,8 @@ func parsePCFResponse(buf []byte) ([]*ibmmq.PCFParameter, bool) { var elemList []*ibmmq.PCFParameter var bytesRead int + traceEntry("parsePCFResponse") + rc := false // First get the MQCFH structure. This also returns @@ -1088,6 +1167,7 @@ func parsePCFResponse(buf []byte) ([]*ibmmq.PCFParameter, bool) { // looking for the next element cfh, offset := ibmmq.ReadPCFHeader(buf) if cfh == nil { + traceExit("parsePCFResponse", 1) return elemList, true } @@ -1115,6 +1195,8 @@ func parsePCFResponse(buf []byte) ([]*ibmmq.PCFParameter, bool) { if cfh.Control == ibmmq.MQCFC_LAST { rc = true } + traceExit("parsePCFResponse", 0) + return elemList, rc } @@ -1179,7 +1261,7 @@ func formatDescription(elem *MonElement) string { s = s + "_count" } } - + logTrace(" [%s] in:%s out:%s", "formatDescription", elem.Description, s) return s } diff --git a/mqmetric/log.go b/mqmetric/log.go index 2643c04..e91bb6c 100644 --- a/mqmetric/log.go +++ b/mqmetric/log.go @@ -1,7 +1,7 @@ package mqmetric /* - Copyright (c) IBM Corporation 2016, 2019 + Copyright (c) IBM Corporation 2016, 2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,11 +23,16 @@ import ( "fmt" ) +// Setup for the 7 levels of logging that logrus allows, even if we don't +// intend to use all of them for now. type Logger struct { + Trace func(string, ...interface{}) Debug func(string, ...interface{}) Info func(string, ...interface{}) Warn func(string, ...interface{}) Error func(string, ...interface{}) + Fatal func(string, ...interface{}) + Panic func(string, ...interface{}) } var logger *Logger = nil @@ -36,6 +41,11 @@ func SetLogger(l *Logger) { logger = l } +func logTrace(format string, v ...interface{}) { + if logger != nil && logger.Trace != nil { + logger.Trace(format, v...) + } +} func logDebug(format string, v ...interface{}) { if logger != nil && logger.Debug != nil { logger.Debug(format, v...) @@ -61,3 +71,60 @@ func logError(format string, v ...interface{}) { fmt.Printf(format, v...) } } + +// Panic and Fatal are not going to be used for now but +// are in here for completeness +func logFatal(format string, v ...interface{}) { + if logger != nil && logger.Fatal != nil { + logger.Fatal(format, v...) + } +} +func logPanic(format string, v ...interface{}) { + if logger != nil && logger.Panic != nil { + logger.Panic(format, v...) + } +} + +// Some interfaces to enable tracing. In its simplest form, tracing the +// entry point just needs the function name. There are often several exit +// points from functions when we short-circuit via early parameter tests, +// so we make them unique with a mandatory returnPoint value. +// More sophisticated tracing of input and output values can be done with the +// EntryF and ExitF functions that take the usual formatting strings. +func traceEntry(f string) { + traceEntryF(f, "") +} +func traceEntryF(f string, format string, v ...interface{}) { + if format != "" { + fs := make([]interface{}, 1) + fs[0] = f + logTrace("> [%s] : "+format, append(fs, v)...) + } else { + logTrace("> [%s]", f) + } +} + +func traceExit(f string, returnPoint int) { + traceExitF(f, returnPoint, "") +} +func traceExitErr(f string, returnPoint int, err error) { + if err == nil { + traceExitF(f, returnPoint, "Error: nil") + } else { + traceExitF(f, returnPoint, "Error: %v", err) + } +} + +func traceExitF(f string, returnPoint int, format string, v ...interface{}) { + if format != "" { + fs := make([]interface{}, 2) + fs[0] = f + fs[1] = returnPoint + if len(v) > 0 { + fs = append(fs, v) + } + logTrace("< [%s] rp: %d "+format, fs...) + } else { + logTrace("< [%s] rp: %d", f, returnPoint) + } +} diff --git a/mqmetric/mqif.go b/mqmetric/mqif.go index 02ecf38..611e80c 100644 --- a/mqmetric/mqif.go +++ b/mqmetric/mqif.go @@ -1,7 +1,7 @@ package mqmetric /* - Copyright (c) IBM Corporation 2016, 2019 + Copyright (c) IBM Corporation 2016, 2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -107,6 +107,8 @@ func InitConnection(qMgrName string, replyQ string, cc *ConnectionConfig) error var mqreturn *ibmmq.MQReturn var errorString = "" + traceEntryF("InitConnection", "QMgrName %s", qMgrName) + gocno := ibmmq.NewMQCNO() gocsp := ibmmq.NewMQCSP() @@ -271,9 +273,12 @@ func InitConnection(qMgrName string, replyQ string, cc *ConnectionConfig) error } if err != nil { + traceExitErr("InitConnection", 1, mqreturn) return MQMetricError{Err: errorString, MQReturn: mqreturn} } + traceExitErr("InitConnection", 0, mqreturn) + return err } @@ -281,7 +286,7 @@ func InitConnection(qMgrName string, replyQ string, cc *ConnectionConfig) error EndConnection tidies up by closing the queues and disconnecting. */ func EndConnection() { - + traceEntry("EndConnection") // MQCLOSE all subscriptions if subsOpened { for _, cl := range Metrics.Classes { @@ -306,6 +311,7 @@ func EndConnection() { qMgr.Disc() } + traceExit("EndConnection", 0) } /* @@ -319,13 +325,17 @@ A 32K buffer was created at the top of this file, and should always be big enough for what we are expecting. */ func getMessage(wait bool) ([]byte, error) { - return getMessageWithHObj(wait, replyQObj) + traceEntry("getMessage") + rc, err := getMessageWithHObj(wait, replyQObj) + traceExitErr("getMessage", 0, err) + return rc, err } func getMessageWithHObj(wait bool, hObj ibmmq.MQObject) ([]byte, error) { var err error var datalen int + traceEntry("getMessageWithHObj") getmqmd := ibmmq.NewMQMD() gmo := ibmmq.NewMQGMO() gmo.Options = ibmmq.MQGMO_NO_SYNCPOINT @@ -341,6 +351,8 @@ func getMessageWithHObj(wait bool, hObj ibmmq.MQObject) ([]byte, error) { datalen, err = hObj.Get(getmqmd, gmo, getBuffer) + traceExitErr("getMessageWithHObj", 0, err) + return getBuffer[0:datalen], err } @@ -365,6 +377,7 @@ func subscribeManaged(topic string, pubQObj *ibmmq.MQObject) (ibmmq.MQObject, er func subscribeWithOptions(topic string, pubQObj *ibmmq.MQObject, managed bool) (ibmmq.MQObject, error) { var err error + traceEntry("subscribeWithOptions") mqsd := ibmmq.NewMQSD() mqsd.Options = ibmmq.MQSO_CREATE mqsd.Options |= ibmmq.MQSO_NON_DURABLE @@ -383,9 +396,12 @@ func subscribeWithOptions(topic string, pubQObj *ibmmq.MQObject, managed bool) ( case ibmmq.MQRC_HANDLE_NOT_AVAILABLE: extraInfo = "You may need to increase the MAXHANDS attribute on the queue manager." } - return subObj, fmt.Errorf("Error subscribing to topic '%s': %v %s", topic, err, extraInfo) + e2 := fmt.Errorf("Error subscribing to topic '%s': %v %s", topic, err, extraInfo) + traceExitErr("subscribeWithOptions", 1, e2) + return subObj, e2 } + traceExitErr("subscribeWithOptions", 0, err) return subObj, err } diff --git a/mqmetric/qmgr.go b/mqmetric/qmgr.go index 1aad9ad..84fba16 100644 --- a/mqmetric/qmgr.go +++ b/mqmetric/qmgr.go @@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB. package mqmetric /* - Copyright (c) IBM Corporation 2018,2019 + Copyright (c) IBM Corporation 2018,2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -59,7 +59,10 @@ text. The elements can be expanded later; just trying to give a starting point for now. */ func QueueManagerInitAttributes() { + + traceEntry("QueueManagerInitAttributes") if qMgrAttrsInit { + traceExit("QueueManagerInitAttributes", 1) return } QueueManagerStatus.Attributes = make(map[string]*StatusAttribute) @@ -93,11 +96,15 @@ func QueueManagerInitAttributes() { } qMgrAttrsInit = true + + traceExit("QueueManagerInitAttributes", 0) + } func CollectQueueManagerStatus() error { var err error + traceEntry("CollectQueueManagerStatus") QueueManagerInitAttributes() for k := range QueueManagerStatus.Attributes { QueueManagerStatus.Attributes[k].Values = make(map[string]*StatusValue) @@ -109,6 +116,9 @@ func CollectQueueManagerStatus() error { } else { err = collectQueueManagerStatus(ibmmq.MQOT_Q_MGR) } + + traceExitErr("CollectQueueManagerStatus", 0, err) + return err } @@ -117,6 +127,9 @@ func CollectQueueManagerStatus() error { // They can be obtained via MQINQ and do not need a PCF flow. // We can't get these on Distributed because equivalents are in qm.ini func collectQueueManagerAttrs() error { + + traceEntry("collectQueueManagerAttrs") + selectors := []int32{ibmmq.MQCA_Q_MGR_NAME, ibmmq.MQIA_ACTIVE_CHANNELS, ibmmq.MQIA_TCP_CHANNELS, @@ -134,6 +147,8 @@ func collectQueueManagerAttrs() error { QueueManagerStatus.Attributes[ATTR_QMGR_NAME].Values[key] = newStatusValueString(key) } + traceExitErr("collectQueueManagerAttrs", 0, err) + return err } @@ -142,6 +157,8 @@ func collectQueueManagerAttrs() error { func collectQueueManagerStatus(instanceType int32) error { var err error + traceEntry("collectQueueManagerStatus") + statusClearReplyQ() putmqmd, pmo, cfh, buf := statusSetCommandHeaders() @@ -155,6 +172,7 @@ func collectQueueManagerStatus(instanceType int32) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("collectQueueManagerStatus", 1, err) return err } @@ -165,9 +183,9 @@ func collectQueueManagerStatus(instanceType int32) error { if buf != nil { parseQMgrData(instanceType, cfh, buf) } - } + traceExitErr("collectQueueManagerStatus", 0, err) return err } @@ -175,6 +193,7 @@ func collectQueueManagerStatus(instanceType int32) error { func parseQMgrData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter + traceEntry("parseQMgrData") qMgrName := "" key := "" @@ -186,6 +205,7 @@ func parseQMgrData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseQMgrData", 1) return "" } @@ -233,6 +253,7 @@ func parseQMgrData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { now := time.Now() QueueManagerStatus.Attributes[ATTR_QMGR_UPTIME].Values[key] = newStatusValueInt64(statusTimeDiff(now, startDate, startTime)) + traceExitF("parseQMgrData", 0, "Key: %s", key) return key } diff --git a/mqmetric/queue.go b/mqmetric/queue.go index edbb05d..6f397e2 100644 --- a/mqmetric/queue.go +++ b/mqmetric/queue.go @@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB. package mqmetric /* - Copyright (c) IBM Corporation 2018,2019 + Copyright (c) IBM Corporation 2018,2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -75,7 +75,9 @@ text. The elements can be expanded later; just trying to give a starting point for now. */ func QueueInitAttributes() { + traceEntry("QueueInitAttributes") if qAttrsInit { + traceExit("QueueInitAttributes", 1) return } QueueStatus.Attributes = make(map[string]*StatusAttribute) @@ -138,18 +140,25 @@ func QueueInitAttributes() { QueueStatus.Attributes[attr].index = 1 qAttrsInit = true + + traceExit("QueueInitAttributes", 0) + } // If we need to list the queues that match a pattern. Not needed for // the status queries as they (unlike the pub/sub resource stats) accept // patterns in the PCF command func InquireQueues(patterns string) ([]string, error) { + traceEntry("InquireQueues") QueueInitAttributes() - return inquireObjects(patterns, ibmmq.MQOT_Q) + rc, err := inquireObjects(patterns, ibmmq.MQOT_Q) + traceExitErr("InquireQueues", 0, err) + return rc, err } func CollectQueueStatus(patterns string) error { var err error + traceEntry("CollectQueueStatus") QueueInitAttributes() @@ -160,6 +169,7 @@ func CollectQueueStatus(patterns string) error { queuePatterns := strings.Split(patterns, ",") if len(queuePatterns) == 0 { + traceExit("CollectQueueStatus", 1) return nil } @@ -189,6 +199,7 @@ func CollectQueueStatus(patterns string) error { } } } + traceExitErr("CollectQueueStatus", 0, err) return err } @@ -196,6 +207,7 @@ func CollectQueueStatus(patterns string) error { // Collect the responses and build up the statistics func collectQueueStatus(pattern string, instanceType int32) error { var err error + traceEntryF("collectQueueStatus", "Pattern: %s", pattern) statusClearReplyQ() @@ -226,6 +238,7 @@ func collectQueueStatus(pattern string, instanceType int32) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExit("collectQueueStatus", 1) return err } @@ -238,12 +251,14 @@ func collectQueueStatus(pattern string, instanceType int32) error { } } + traceExitErr("collectQueueStatus", 0, err) return err } func collectResetQStats(pattern string) error { var err error + traceEntry("collectResetQStats") statusClearReplyQ() putmqmd, pmo, cfh, buf := statusSetCommandHeaders() @@ -263,6 +278,7 @@ func collectResetQStats(pattern string) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("collectResetQueueStats", 1, err) return err } @@ -274,7 +290,7 @@ func collectResetQStats(pattern string) error { parseResetQStatsData(cfh, buf) } } - + traceExitErr("collectResetQueueStats", 0, err) return err } @@ -284,9 +300,11 @@ func collectResetQStats(pattern string) error { func inquireQueueAttributes(objectPatternsList string) error { var err error + traceEntry("inquireQueueAttributes") statusClearReplyQ() if objectPatternsList == "" { + traceExitErr("inquireQueueAttributes", 1, err) return err } @@ -326,6 +344,7 @@ func inquireQueueAttributes(objectPatternsList string) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("inquireQueueAttributes", 2, err) return err } @@ -336,6 +355,7 @@ func inquireQueueAttributes(objectPatternsList string) error { } } } + traceExit("inquireQueueAttributes", 0) return nil } @@ -343,6 +363,7 @@ func inquireQueueAttributes(objectPatternsList string) error { func parseQData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter + traceEntry("parseQData") qName := "" key := "" @@ -356,6 +377,7 @@ func parseQData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseQData", 1) return "" } @@ -413,6 +435,7 @@ func parseQData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { usage := s.AttrUsage QueueStatus.Attributes[ATTR_Q_USAGE].Values[key] = newStatusValueInt64(usage) } + traceExitF("parseQData", 0, "Key: %s", key) return key } @@ -420,6 +443,7 @@ func parseQData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { func parseResetQStatsData(cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter + traceEntry("parseResetQStatsData") qName := "" key := "" @@ -428,6 +452,7 @@ func parseResetQStatsData(cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseResetQStatsData", 1) return "" } @@ -466,12 +491,13 @@ func parseResetQStatsData(cfh *ibmmq.MQCFH, buf []byte) string { statusGetIntAttributes(QueueStatus, elem, key) } + traceExitF("parseResetQStatsData", 0, "Key: %s", key) return key } func parseQAttrData(cfh *ibmmq.MQCFH, buf []byte) { var elem *ibmmq.PCFParameter - + traceEntry("parseQAttrData") qName := "" parmAvail := true @@ -479,6 +505,7 @@ func parseQAttrData(cfh *ibmmq.MQCFH, buf []byte) { offset := 0 datalen := len(buf) if cfh.ParameterCount == 0 { + traceExit("parseQAttrData", 1) return } // Parse it once to extract the fields that are needed for the map key @@ -535,6 +562,7 @@ func parseQAttrData(cfh *ibmmq.MQCFH, buf []byte) { } + traceExit("parseQAttrData", 0) return } diff --git a/mqmetric/status.go b/mqmetric/status.go index 51adfa6..7d18fa7 100644 --- a/mqmetric/status.go +++ b/mqmetric/status.go @@ -112,6 +112,8 @@ func statusTimeDiff(now time.Time, d string, t string) int64 { var err error var parsedT time.Time + traceEntry("statusTimeDiff") + // If there's any error in parsing the timestamp - perhaps // the value has not been set yet, then just return 0 rc = 0 @@ -136,10 +138,12 @@ func statusTimeDiff(now time.Time, d string, t string) int64 { } } //fmt.Printf("statusTimeDiff d:%s t:%s diff:%d tzoffset: %f err:%v\n", d, t, rc, tzOffsetSecs, err) + traceExitF("statusTimeDiff", 0, "Diff: %d", rc) return rc } func statusClearReplyQ() { + traceEntry("statusClearReplyQ") buf := make([]byte, 0) // Empty replyQ in case any left over from previous errors for ok := true; ok; { @@ -156,6 +160,7 @@ func statusClearReplyQ() { ok = false } } + traceExit("statusClearReplyQ", 0) return } @@ -163,6 +168,7 @@ func statusClearReplyQ() { // server. The caller of this function will complete the message contents // with elements specific to the object type. func statusSetCommandHeaders() (*ibmmq.MQMD, *ibmmq.MQPMO, *ibmmq.MQCFH, []byte) { + traceEntry("statusSetCommandHeaders") cfh := ibmmq.NewMQCFH() cfh.Version = ibmmq.MQCFH_VERSION_3 cfh.Type = ibmmq.MQCFT_COMMAND_XR @@ -182,6 +188,8 @@ func statusSetCommandHeaders() (*ibmmq.MQMD, *ibmmq.MQPMO, *ibmmq.MQCFH, []byte) buf := make([]byte, 0) + traceExit("statusSetCommandHeaders", 0) + return putmqmd, pmo, cfh, buf } @@ -192,6 +200,7 @@ func statusGetReply() (*ibmmq.MQCFH, []byte, bool, error) { var offset int var cfh *ibmmq.MQCFH + traceEntry("statusGetReply") replyBuf := make([]byte, 10240) getmqmd := ibmmq.NewMQMD() @@ -212,19 +221,22 @@ func statusGetReply() (*ibmmq.MQCFH, []byte, bool, error) { } if cfh.Reason != ibmmq.MQRC_NONE { + traceExitErr("statusGetReply", 1, err) return cfh, nil, allDone, err } // Returned by z/OS qmgrs but are not interesting if cfh.Type == ibmmq.MQCFT_XR_SUMMARY || cfh.Type == ibmmq.MQCFT_XR_MSG { + traceExitErr("statusGetReply", 2, err) return cfh, nil, allDone, err } } else { if err.(*ibmmq.MQReturn).MQRC != ibmmq.MQRC_NO_MSG_AVAILABLE { logError("StatusGetReply error : %v\n", err) } + traceExitErr("statusGetReply", 3, err) return nil, nil, allDone, err } - + traceExitErr("statusGetReply", 0, err) return cfh, replyBuf[offset:datalen], allDone, err } @@ -232,6 +244,7 @@ func statusGetReply() (*ibmmq.MQCFH, []byte, bool, error) { // server messages. We can deal here with the various integer responses; string // responses need to be handled in the object-specific caller. func statusGetIntAttributes(s StatusSet, elem *ibmmq.PCFParameter, key string) bool { + // traceEntry("statusGetIntAttributes") // Don't trace as too noisy usableValue := false if elem.Type == ibmmq.MQCFT_INTEGER || elem.Type == ibmmq.MQCFT_INTEGER64 || elem.Type == ibmmq.MQCFT_INTEGER_LIST || elem.Type == ibmmq.MQCFT_INTEGER64_LIST { @@ -239,6 +252,7 @@ func statusGetIntAttributes(s StatusSet, elem *ibmmq.PCFParameter, key string) b } if !usableValue { + //traceExit("statusGetIntAttributes", 1) return false } @@ -289,6 +303,7 @@ func statusGetIntAttributes(s StatusSet, elem *ibmmq.PCFParameter, key string) b } } + //traceExit("statusGetIntAttributes", 0) return true } diff --git a/mqmetric/sub.go b/mqmetric/sub.go index e7d9d6f..9b18e73 100644 --- a/mqmetric/sub.go +++ b/mqmetric/sub.go @@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB. package mqmetric /* - Copyright (c) IBM Corporation 2018,2019 + Copyright (c) IBM Corporation 2018,2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -57,7 +57,9 @@ text. The elements can be expanded later; just trying to give a starting point for now. */ func SubInitAttributes() { + traceEntry("SubInitAttributes") if subAttrsInit { + traceExit("SubInitAttributes", 1) return } SubStatus.Attributes = make(map[string]*StatusAttribute) @@ -81,11 +83,12 @@ func SubInitAttributes() { SubStatus.Attributes[attr].delta = true subAttrsInit = true + traceExit("SubInitAttributes", 0) } func CollectSubStatus(patterns string) error { var err error - + traceEntry("CollectSubStatus") SubInitAttributes() // Empty any collected values @@ -95,6 +98,7 @@ func CollectSubStatus(patterns string) error { subPatterns := strings.Split(patterns, ",") if len(subPatterns) == 0 { + traceExit("CollectSubStatus", 1) return nil } @@ -108,8 +112,9 @@ func CollectSubStatus(patterns string) error { } - return err + traceExitErr("CollectSubStatus", 0, err) + return err } // Issue the INQUIRE_SUB_STATUS command for a subscription name pattern @@ -117,6 +122,7 @@ func CollectSubStatus(patterns string) error { func collectSubStatus(pattern string) error { var err error + traceEntryF("collectSubStatus", "Pattern: %s", pattern) statusClearReplyQ() putmqmd, pmo, cfh, buf := statusSetCommandHeaders() @@ -139,8 +145,9 @@ func collectSubStatus(pattern string) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { - return err + traceExitErr("collectSubStatus", 1, err) + return err } // Now get the responses - loop until all have been received (one @@ -152,6 +159,8 @@ func collectSubStatus(pattern string) error { } } + traceExitErr("collectSubStatus", 0, err) + return err } @@ -159,6 +168,7 @@ func collectSubStatus(pattern string) error { func parseSubData(cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter + traceEntry("parseSubData") subName := "" subId := "" key := "" @@ -172,6 +182,7 @@ func parseSubData(cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseSubData", 1) return "" } @@ -227,6 +238,8 @@ func parseSubData(cfh *ibmmq.MQCFH, buf []byte) string { SubStatus.Attributes[ATTR_SUB_TOPIC_STRING].Values[key] = newStatusValueString(topicString) SubStatus.Attributes[ATTR_SUB_NAME].Values[key] = newStatusValueString(subName) + traceExitF("parseSubData", 0, "Key : %s", key) + return key } diff --git a/mqmetric/topic.go b/mqmetric/topic.go index 9ac3a7d..cd1b5ca 100644 --- a/mqmetric/topic.go +++ b/mqmetric/topic.go @@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB. package mqmetric /* - Copyright (c) IBM Corporation 2016, 2019 + Copyright (c) IBM Corporation 2016, 2020 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -59,7 +59,9 @@ text. The elements can be expanded later; just trying to give a starting point for now. */ func TopicInitAttributes() { + traceEntry("TopicInitAttributes") if tpAttrsInit { + traceExit("TopicInitAttributes", 1) return } TopicStatus.Attributes = make(map[string]*StatusAttribute) @@ -90,18 +92,24 @@ func TopicInitAttributes() { TopicStatus.Attributes[attr] = newStatusAttribute(attr, "Time Since Msg", -1) tpAttrsInit = true + traceExit("TopicInitAttributes", 0) + } // If we need to list the topics that match a pattern. Not needed for // the status queries as they (unlike the pub/sub resource stats) accept // patterns in the PCF command func InquireTopics(patterns string) ([]string, error) { + traceEntry("InquireTopics") TopicInitAttributes() - return inquireObjects(patterns, ibmmq.MQOT_TOPIC) + rc, err := inquireObjects(patterns, ibmmq.MQOT_TOPIC) + traceExitErr("InquireTopics", 0, err) + return rc, err } func CollectTopicStatus(patterns string) error { var err error + traceEntry("CollectTopicStatus") topicsSeen = make(map[string]bool) // Record which topics have been seen in this period TopicInitAttributes() @@ -112,6 +120,7 @@ func CollectTopicStatus(patterns string) error { topicPatterns := strings.Split(patterns, ",") if len(topicPatterns) == 0 { + traceExit("CollectTopicStatus", 1) return nil } @@ -153,6 +162,8 @@ func CollectTopicStatus(patterns string) error { } } + traceExitErr("CollectTopicStatus", 0, err) + return err } @@ -160,6 +171,7 @@ func CollectTopicStatus(patterns string) error { // Collect the responses and build up the statistics func collectTopicStatus(pattern string, instanceType int32) error { var err error + traceEntryF("collectTopicStatus", "Pattern: %s", pattern) statusClearReplyQ() putmqmd, pmo, cfh, buf := statusSetCommandHeaders() @@ -189,6 +201,7 @@ func collectTopicStatus(pattern string, instanceType int32) error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("collectTopicStatus", 1, err) return err } @@ -204,13 +217,15 @@ func collectTopicStatus(pattern string, instanceType int32) error { } + traceExitErr("collectTopicStatus", 0, err) + return err } // Given a PCF response message, parse it to extract the desired statistics func parseTopicData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter - + traceEntry("parseTopicData") tpName := "" key := "" @@ -223,6 +238,7 @@ func parseTopicData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseTopicData", 1) return "" } @@ -292,6 +308,8 @@ func parseTopicData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string { } } + traceExitF("parseTopicData", 0, "Key: %s", key) + return key } @@ -304,7 +322,7 @@ func TopicNormalise(attr *StatusAttribute, v int64) float64 { // Return a combination of the topic name and the status query type so we // get unique keys in the map. There might be valid data for the same -// topic name in TYPE(SUB), TYPE(TOPIC) and TYPE(TOPIC). +// topic name in TYPE(SUB) and TYPE(TOPIC). func TopicKey(n string, t string) string { return n + "[!" + t + "!]" } diff --git a/mqmetric/usage.go b/mqmetric/usage.go index 72df1a6..65e011e 100644 --- a/mqmetric/usage.go +++ b/mqmetric/usage.go @@ -58,7 +58,9 @@ var UsagePsStatus StatusSet var usageAttrsInit = false func UsageInitAttributes() { + traceEntry("usageInitAttributes") if usageAttrsInit { + traceExit("usageInitAttributes", 1) return } UsageBpStatus.Attributes = make(map[string]*StatusAttribute) @@ -97,11 +99,13 @@ func UsageInitAttributes() { UsagePsStatus.Attributes[attr] = newStatusAttribute(attr, "Expansion Count", ibmmq.MQIACF_USAGE_EXPAND_COUNT) usageAttrsInit = true + traceExit("usageInitAttributes", 0) + } func CollectUsageStatus() error { var err error - + traceEntry("CollectUsageStatus") UsageInitAttributes() // Empty any collected values @@ -112,12 +116,14 @@ func CollectUsageStatus() error { UsagePsStatus.Attributes[k].Values = make(map[string]*StatusValue) } err = collectUsageStatus() - + traceExitErr("CollectUsageStatus", 0, err) return err } func collectUsageStatus() error { var err error + traceEntry("collectUsageStatus") + statusClearReplyQ() putmqmd, pmo, cfh, buf := statusSetCommandHeaders() @@ -134,6 +140,7 @@ func collectUsageStatus() error { // And now put the command to the queue err = cmdQObj.Put(putmqmd, pmo, buf) if err != nil { + traceExitErr("collectUsageStatus", 1, err) return err } @@ -147,6 +154,8 @@ func collectUsageStatus() error { } + traceExitErr("collectUsageStatus", 0, err) + return err } @@ -154,6 +163,8 @@ func collectUsageStatus() error { func parseUsageData(cfh *ibmmq.MQCFH, buf []byte) string { var elem *ibmmq.PCFParameter var responseType int32 + + traceEntry("parseUsageData") bpId := "" bpLocation := "" bpClass := "" @@ -165,6 +176,7 @@ func parseUsageData(cfh *ibmmq.MQCFH, buf []byte) string { offset := 0 datalen := len(buf) if cfh == nil || cfh.ParameterCount == 0 { + traceExit("parseUsageData", 1) return "" } @@ -184,6 +196,7 @@ func parseUsageData(cfh *ibmmq.MQCFH, buf []byte) string { case ibmmq.MQIACF_USAGE_BUFFER_POOL, ibmmq.MQIACF_USAGE_PAGESET: responseType = v default: + traceExit("parseUsageData", 2) return "" } @@ -261,6 +274,7 @@ func parseUsageData(cfh *ibmmq.MQCFH, buf []byte) string { statusGetIntAttributes(UsagePsStatus, elem, key) } } + traceExitF("parseUsageData", 0, "Key: %s", key) return key }