File: System\ServiceModel\Diagnostics\ServicePerformanceCountersBase.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Diagnostics
{
    using System.Diagnostics;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Administration;
    using System.Diagnostics.PerformanceData;
 
    abstract class ServicePerformanceCountersBase : PerformanceCountersBase
    {
        string instanceName;
 
        internal enum PerfCounters : int
        {
            Calls = 0,
            CallsPerSecond,
            CallsOutstanding,
            CallsFailed,
            CallsFailedPerSecond,
            CallsFaulted,
            CallsFaultedPerSecond,
            CallDuration,
            CallDurationBase,
            SecurityValidationAuthenticationFailures,
            SecurityValidationAuthenticationFailuresPerSecond,
            CallsNotAuthorized,
            CallsNotAuthorizedPerSecond,
            Instances,
            InstancesRate,
            RMSessionsFaulted,
            RMSessionsFaultedPerSecond,
            RMMessagesDropped,
            RMMessagesDroppedPerSecond,
            TxFlowed,
            TxFlowedPerSecond,
            TxCommitted,
            TxCommittedPerSecond,
            TxAborted,
            TxAbortedPerSecond,
            TxInDoubt,
            TxInDoubtPerSecond,
            MsmqPoisonMessages,
            MsmqPoisonMessagesPerSecond,
            MsmqRejectedMessages,
            MsmqRejectedMessagesPerSecond,
            MsmqDroppedMessages,
            MsmqDroppedMessagesPerSecond,
            CallsPercentMaxCalls,
            CallsPercentMaxCallsBase,
            InstancesPercentMaxInstances,
            InstancesPercentMaxInstancesBase,
            SessionsPercentMaxSessions,
            SessionsPercentMaxSessionsBase,
            TotalCounters = SessionsPercentMaxSessionsBase + 1
        }
 
        protected static readonly string[] perfCounterNames = 
        {
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCalls,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallsPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallsOutstanding,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallsFailed,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallsFailedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallsFaulted,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallsFaultedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallDuration,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SCallDurationBase,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SSecurityValidationAuthenticationFailures,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SSecurityValidationAuthenticationFailuresPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SSecurityCallsNotAuthorized,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SSecurityCallsNotAuthorizedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SInstances,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SInstancesPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SRMSessionsFaulted,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SRMSessionsFaultedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SRMMessagesDropped,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SRMMessagesDroppedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxFlowed,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxFlowedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxCommitted,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxCommittedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxAborted,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxAbortedPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxInDoubt,
            PerformanceCounterStrings.SERVICEMODELSERVICE.STxInDoubtPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.MsmqPoisonMessages,
            PerformanceCounterStrings.SERVICEMODELSERVICE.MsmqPoisonMessagesPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.MsmqRejectedMessages,
            PerformanceCounterStrings.SERVICEMODELSERVICE.MsmqRejectedMessagesPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.MsmqDroppedMessages,
            PerformanceCounterStrings.SERVICEMODELSERVICE.MsmqDroppedMessagesPerSecond,
            PerformanceCounterStrings.SERVICEMODELSERVICE.CallsPercentMaxConcurrentCalls,
            PerformanceCounterStrings.SERVICEMODELSERVICE.CallsPercentMaxConcurrentCallsBase,
            PerformanceCounterStrings.SERVICEMODELSERVICE.InstancesPercentMaxConcurrentInstances,
            PerformanceCounterStrings.SERVICEMODELSERVICE.InstancesPercentMaxConcurrentInstancesBase,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SessionsPercentMaxConcurrentSessions,
            PerformanceCounterStrings.SERVICEMODELSERVICE.SessionsPercentMaxConcurrentSessionsBase,
        };
 
        const int maxCounterLength = 64;
        const int hashLength = 2;
        [Flags]
        enum truncOptions : uint
        {
            NoBits = 0,
            service32 = 0x01,
            uri31 = 0x04
        }
 
        internal ServicePerformanceCountersBase(ServiceHostBase serviceHost)
        {
            this.instanceName = CreateFriendlyInstanceName(serviceHost);
        }
 
        internal override string InstanceName
        {
            get
            {
                return this.instanceName;
            }
        }
 
        internal override string[] CounterNames
        {
            get
            {
                return perfCounterNames;
            }
        }
 
        internal override int PerfCounterStart
        {
            get { return (int)PerfCounters.Calls; }
        }
 
        internal override int PerfCounterEnd
        {
            get { return (int)PerfCounters.TotalCounters; }
        }
 
        private static string GetServiceUri(ServiceHostBase serviceHost, ServiceInfo serviceInfo)
        {
            string uri;
            if (!TryGetFullVirtualPath(serviceHost, out uri))
            {
                uri = serviceInfo.FirstAddress;
            }
            return uri;
        }
 
        private static string GetFullInstanceName(ServiceHostBase serviceHost)
        {
            // instance name is: serviceName@uri
            ServiceInfo serviceInfo = new ServiceInfo(serviceHost);
            string serviceName = serviceInfo.ServiceName;
            string uri = GetServiceUri(serviceHost, serviceInfo);
            return String.Format("{0}@{1}", serviceName, uri);
        }
 
        private static string GetShortInstanceName(ServiceHostBase serviceHost)
        {
            ServiceInfo serviceInfo = new ServiceInfo(serviceHost);
            string serviceName = serviceInfo.ServiceName;
            string uri = GetServiceUri(serviceHost, serviceInfo);
 
            int length = serviceName.Length + uri.Length + 2;
 
            if (length > maxCounterLength)
            {
                int count = 0;
 
                truncOptions tasks = ServicePerformanceCountersBase.GetCompressionTasks(
                    length, serviceName.Length, uri.Length);
 
                //if necessary, compress service name to 8 chars with a 2 char hash code
                if ((tasks & truncOptions.service32) > 0)
                {
                    count = 32;
                    serviceName = GetHashedString(serviceName, count - hashLength, serviceName.Length - count + hashLength, true);
                }
 
                //if necessary,  compress uri to 36 chars with a 2 char hash code
                if ((tasks & truncOptions.uri31) > 0)
                {
                    count = 31;
                    uri = GetHashedString(uri, 0, uri.Length - count + hashLength, false);
                }
            }
 
            // replace '/' with '|' because perfmon fails when '/' is in perfcounter instance name
            return serviceName + "@" + uri.Replace('/', '|');
        }
 
        internal static string CreateFriendlyInstanceName(ServiceHostBase serviceHost)
        {
            string shortInstanceName = GetShortInstanceName(serviceHost);
            if (!ServiceModelAppSettings.EnsureUniquePerformanceCounterInstanceNames)
            {
                return shortInstanceName;
            }
 
            string fullInstanceName = GetFullInstanceName(serviceHost);
 
            return EnsureUniqueInstanceName(PerformanceCounterStrings.SERVICEMODELSERVICE.ServicePerfCounters, shortInstanceName, fullInstanceName);
        }
 
        internal static string GetFriendlyInstanceName(ServiceHostBase serviceHost)
        {
            string shortInstanceName = GetShortInstanceName(serviceHost);
            if (!ServiceModelAppSettings.EnsureUniquePerformanceCounterInstanceNames)
            {
                return shortInstanceName;
            }
 
            string fullInstanceName = GetFullInstanceName(serviceHost);
 
            return GetUniqueInstanceName(PerformanceCounterStrings.SERVICEMODELSERVICE.ServicePerfCounters, shortInstanceName, fullInstanceName);
        }
 
        static bool TryGetFullVirtualPath(ServiceHostBase serviceHost, out string uri)
        {
            VirtualPathExtension pathExtension = serviceHost.Extensions.Find<VirtualPathExtension>();
            if (pathExtension == null)
            {
                uri = null;
                return false;
            }
            uri = pathExtension.ApplicationVirtualPath + pathExtension.VirtualPath.ToString().Replace("~", "");
            return uri != null;
        }
 
        static truncOptions GetCompressionTasks(int totalLen, int serviceLen, int uriLen)
        {
            truncOptions bitmask = 0;
 
            if (totalLen > maxCounterLength)
            {
                int workingLen = totalLen;
 
                //note: order of if statements important (see spec)!
                if (workingLen > maxCounterLength && serviceLen > 32)
                {
                    bitmask |= truncOptions.service32; //compress service name to 16 chars
                    workingLen -= serviceLen - 32;
                }
                if (workingLen > maxCounterLength && uriLen > 31)
                {
                    bitmask |= truncOptions.uri31; //compress uri to 31 chars
                }
            }
 
            return bitmask;
        }
 
        internal abstract void MethodCalled();
 
        internal abstract void MethodReturnedSuccess();
 
        internal abstract void MethodReturnedError();
 
        internal abstract void MethodReturnedFault();
 
        internal abstract void SaveCallDuration(long time);
 
        internal abstract void AuthenticationFailed();
 
        internal abstract void AuthorizationFailed();
 
        internal abstract void ServiceInstanceCreated();
 
        internal abstract void ServiceInstanceRemoved();
 
        internal abstract void SessionFaulted();
 
        internal abstract void MessageDropped();
 
        internal abstract void TxCommitted(long count);
 
        internal abstract void TxInDoubt(long count);
 
        internal abstract void TxAborted(long count);
 
        internal abstract void TxFlowed();
 
        internal abstract void MsmqDroppedMessage();
 
        internal abstract void MsmqPoisonMessage();
 
        internal abstract void MsmqRejectedMessage();
 
        internal abstract void IncrementThrottlePercent(int counterIndex);
 
        internal abstract void SetThrottleBase(int counterIndex, long denominator);
 
        internal abstract void DecrementThrottlePercent(int counterIndex);
    }
}