comparison mercurial/util.py @ 37047:d3a9036d9ae9

util: don't log low-level I/O calls for HTTP peer `hg debugwireproto` is useful for testing HTTP interactions. Possibly more useful than `get-with-headers.py`. But one thing that makes it annoying for mid-level tests is that it logs all API calls, such as readline(). This makes output - especially headers - overly verbose. This commit teaches our file and socket observers to not log API calls on functions dealing with data. We change the behavior of `hg debugwireproto` to enable this mode by default. --debug can be added to restore the previous behavior. As the test changes demonstrate, this makes tests much easier to read. As a bonus, it also removes some required (glob) over lengths in system call results. One thing that's lacking is knowing which side sent data. But we can fix this in a follow-up once it becomes a problem. Differential Revision: https://phab.mercurial-scm.org/D2842
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 13 Mar 2018 11:20:07 -0700
parents 8453699a1f21
children 8c3c47362934
comparison
equal deleted inserted replaced
37046:a8d8cdafe29c 37047:d3a9036d9ae9
760 # proxy using our observer. 760 # proxy using our observer.
761 observer = object.__getattribute__(self, r'_observer') 761 observer = object.__getattribute__(self, r'_observer')
762 return makeloggingfileobject(observer.fh, res, observer.name, 762 return makeloggingfileobject(observer.fh, res, observer.name,
763 reads=observer.reads, 763 reads=observer.reads,
764 writes=observer.writes, 764 writes=observer.writes,
765 logdata=observer.logdata) 765 logdata=observer.logdata,
766 logdataapis=observer.logdataapis)
766 767
767 def recv(self, *args, **kwargs): 768 def recv(self, *args, **kwargs):
768 return object.__getattribute__(self, r'_observedcall')( 769 return object.__getattribute__(self, r'_observedcall')(
769 r'recv', *args, **kwargs) 770 r'recv', *args, **kwargs)
770 771
823 return DATA_ESCAPE_RE.sub(lambda m: DATA_ESCAPE_MAP[m.group(0)], s) 824 return DATA_ESCAPE_RE.sub(lambda m: DATA_ESCAPE_MAP[m.group(0)], s)
824 825
825 class baseproxyobserver(object): 826 class baseproxyobserver(object):
826 def _writedata(self, data): 827 def _writedata(self, data):
827 if not self.logdata: 828 if not self.logdata:
828 self.fh.write('\n') 829 if self.logdataapis:
830 self.fh.write('\n')
831 self.fh.flush()
832 return
833
834 # Simple case writes all data on a single line.
835 if b'\n' not in data:
836 if self.logdataapis:
837 self.fh.write(': %s\n' % escapedata(data))
838 else:
839 self.fh.write('%s> %s\n' % (self.name, escapedata(data)))
829 self.fh.flush() 840 self.fh.flush()
830 return 841 return
831 842
832 # Simple case writes all data on a single line.
833 if b'\n' not in data:
834 self.fh.write(': %s\n' % escapedata(data))
835 self.fh.flush()
836 return
837
838 # Data with newlines is written to multiple lines. 843 # Data with newlines is written to multiple lines.
839 self.fh.write(':\n') 844 if self.logdataapis:
845 self.fh.write(':\n')
846
840 lines = data.splitlines(True) 847 lines = data.splitlines(True)
841 for line in lines: 848 for line in lines:
842 self.fh.write('%s> %s\n' % (self.name, escapedata(line))) 849 self.fh.write('%s> %s\n' % (self.name, escapedata(line)))
843 self.fh.flush() 850 self.fh.flush()
844 851
845 class fileobjectobserver(baseproxyobserver): 852 class fileobjectobserver(baseproxyobserver):
846 """Logs file object activity.""" 853 """Logs file object activity."""
847 def __init__(self, fh, name, reads=True, writes=True, logdata=False): 854 def __init__(self, fh, name, reads=True, writes=True, logdata=False,
855 logdataapis=True):
848 self.fh = fh 856 self.fh = fh
849 self.name = name 857 self.name = name
850 self.logdata = logdata 858 self.logdata = logdata
859 self.logdataapis = logdataapis
851 self.reads = reads 860 self.reads = reads
852 self.writes = writes 861 self.writes = writes
853 862
854 def read(self, res, size=-1): 863 def read(self, res, size=-1):
855 if not self.reads: 864 if not self.reads:
856 return 865 return
857 # Python 3 can return None from reads at EOF instead of empty strings. 866 # Python 3 can return None from reads at EOF instead of empty strings.
858 if res is None: 867 if res is None:
859 res = '' 868 res = ''
860 869
861 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res))) 870 if self.logdataapis:
871 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
872
862 self._writedata(res) 873 self._writedata(res)
863 874
864 def readline(self, res, limit=-1): 875 def readline(self, res, limit=-1):
865 if not self.reads: 876 if not self.reads:
866 return 877 return
867 878
868 self.fh.write('%s> readline() -> %d' % (self.name, len(res))) 879 if self.logdataapis:
880 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
881
869 self._writedata(res) 882 self._writedata(res)
870 883
871 def readinto(self, res, dest): 884 def readinto(self, res, dest):
872 if not self.reads: 885 if not self.reads:
873 return 886 return
874 887
875 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest), 888 if self.logdataapis:
876 res)) 889 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
890 res))
891
877 data = dest[0:res] if res is not None else b'' 892 data = dest[0:res] if res is not None else b''
878 self._writedata(data) 893 self._writedata(data)
879 894
880 def write(self, res, data): 895 def write(self, res, data):
881 if not self.writes: 896 if not self.writes:
884 # Python 2 returns None from some write() calls. Python 3 (reasonably) 899 # Python 2 returns None from some write() calls. Python 3 (reasonably)
885 # returns the integer bytes written. 900 # returns the integer bytes written.
886 if res is None and data: 901 if res is None and data:
887 res = len(data) 902 res = len(data)
888 903
889 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res)) 904 if self.logdataapis:
905 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
906
890 self._writedata(data) 907 self._writedata(data)
891 908
892 def flush(self, res): 909 def flush(self, res):
893 if not self.writes: 910 if not self.writes:
894 return 911 return
895 912
896 self.fh.write('%s> flush() -> %r\n' % (self.name, res)) 913 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
897 914
898 # For observedbufferedinputpipe. 915 # For observedbufferedinputpipe.
899 def bufferedread(self, res, size): 916 def bufferedread(self, res, size):
900 self.fh.write('%s> bufferedread(%d) -> %d' % ( 917 if not self.reads:
901 self.name, size, len(res))) 918 return
919
920 if self.logdataapis:
921 self.fh.write('%s> bufferedread(%d) -> %d' % (
922 self.name, size, len(res)))
923
902 self._writedata(res) 924 self._writedata(res)
903 925
904 def bufferedreadline(self, res): 926 def bufferedreadline(self, res):
905 self.fh.write('%s> bufferedreadline() -> %d' % (self.name, len(res))) 927 if not self.reads:
928 return
929
930 if self.logdataapis:
931 self.fh.write('%s> bufferedreadline() -> %d' % (
932 self.name, len(res)))
933
906 self._writedata(res) 934 self._writedata(res)
907 935
908 def makeloggingfileobject(logh, fh, name, reads=True, writes=True, 936 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
909 logdata=False): 937 logdata=False, logdataapis=True):
910 """Turn a file object into a logging file object.""" 938 """Turn a file object into a logging file object."""
911 939
912 observer = fileobjectobserver(logh, name, reads=reads, writes=writes, 940 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
913 logdata=logdata) 941 logdata=logdata, logdataapis=logdataapis)
914 return fileobjectproxy(fh, observer) 942 return fileobjectproxy(fh, observer)
915 943
916 class socketobserver(baseproxyobserver): 944 class socketobserver(baseproxyobserver):
917 """Logs socket activity.""" 945 """Logs socket activity."""
918 def __init__(self, fh, name, reads=True, writes=True, states=True, 946 def __init__(self, fh, name, reads=True, writes=True, states=True,
919 logdata=False): 947 logdata=False, logdataapis=True):
920 self.fh = fh 948 self.fh = fh
921 self.name = name 949 self.name = name
922 self.reads = reads 950 self.reads = reads
923 self.writes = writes 951 self.writes = writes
924 self.states = states 952 self.states = states
925 self.logdata = logdata 953 self.logdata = logdata
954 self.logdataapis = logdataapis
926 955
927 def makefile(self, res, mode=None, bufsize=None): 956 def makefile(self, res, mode=None, bufsize=None):
928 if not self.states: 957 if not self.states:
929 return 958 return
930 959
933 962
934 def recv(self, res, size, flags=0): 963 def recv(self, res, size, flags=0):
935 if not self.reads: 964 if not self.reads:
936 return 965 return
937 966
938 self.fh.write('%s> recv(%d, %d) -> %d' % ( 967 if self.logdataapis:
939 self.name, size, flags, len(res))) 968 self.fh.write('%s> recv(%d, %d) -> %d' % (
969 self.name, size, flags, len(res)))
940 self._writedata(res) 970 self._writedata(res)
941 971
942 def recvfrom(self, res, size, flags=0): 972 def recvfrom(self, res, size, flags=0):
943 if not self.reads: 973 if not self.reads:
944 return 974 return
945 975
946 self.fh.write('%s> recvfrom(%d, %d) -> %d' % ( 976 if self.logdataapis:
947 self.name, size, flags, len(res[0]))) 977 self.fh.write('%s> recvfrom(%d, %d) -> %d' % (
978 self.name, size, flags, len(res[0])))
979
948 self._writedata(res[0]) 980 self._writedata(res[0])
949 981
950 def recvfrom_into(self, res, buf, size, flags=0): 982 def recvfrom_into(self, res, buf, size, flags=0):
951 if not self.reads: 983 if not self.reads:
952 return 984 return
953 985
954 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % ( 986 if self.logdataapis:
955 self.name, size, flags, res[0])) 987 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % (
988 self.name, size, flags, res[0]))
989
956 self._writedata(buf[0:res[0]]) 990 self._writedata(buf[0:res[0]])
957 991
958 def recv_into(self, res, buf, size=0, flags=0): 992 def recv_into(self, res, buf, size=0, flags=0):
959 if not self.reads: 993 if not self.reads:
960 return 994 return
961 995
962 self.fh.write('%s> recv_into(%d, %d) -> %d' % ( 996 if self.logdataapis:
963 self.name, size, flags, res)) 997 self.fh.write('%s> recv_into(%d, %d) -> %d' % (
998 self.name, size, flags, res))
999
964 self._writedata(buf[0:res]) 1000 self._writedata(buf[0:res])
965 1001
966 def send(self, res, data, flags=0): 1002 def send(self, res, data, flags=0):
967 if not self.writes: 1003 if not self.writes:
968 return 1004 return
973 1009
974 def sendall(self, res, data, flags=0): 1010 def sendall(self, res, data, flags=0):
975 if not self.writes: 1011 if not self.writes:
976 return 1012 return
977 1013
978 # Returns None on success. So don't bother reporting return value. 1014 if self.logdataapis:
979 self.fh.write('%s> sendall(%d, %d)' % ( 1015 # Returns None on success. So don't bother reporting return value.
980 self.name, len(data), flags)) 1016 self.fh.write('%s> sendall(%d, %d)' % (
1017 self.name, len(data), flags))
1018
981 self._writedata(data) 1019 self._writedata(data)
982 1020
983 def sendto(self, res, data, flagsoraddress, address=None): 1021 def sendto(self, res, data, flagsoraddress, address=None):
984 if not self.writes: 1022 if not self.writes:
985 return 1023 return
987 if address: 1025 if address:
988 flags = flagsoraddress 1026 flags = flagsoraddress
989 else: 1027 else:
990 flags = 0 1028 flags = 0
991 1029
992 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % ( 1030 if self.logdataapis:
993 self.name, len(data), flags, address, res)) 1031 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % (
1032 self.name, len(data), flags, address, res))
1033
994 self._writedata(data) 1034 self._writedata(data)
995 1035
996 def setblocking(self, res, flag): 1036 def setblocking(self, res, flag):
997 if not self.states: 1037 if not self.states:
998 return 1038 return
1017 1057
1018 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % ( 1058 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % (
1019 self.name, level, optname, value)) 1059 self.name, level, optname, value))
1020 1060
1021 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True, 1061 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True,
1022 logdata=False): 1062 logdata=False, logdataapis=True):
1023 """Turn a socket into a logging socket.""" 1063 """Turn a socket into a logging socket."""
1024 1064
1025 observer = socketobserver(logh, name, reads=reads, writes=writes, 1065 observer = socketobserver(logh, name, reads=reads, writes=writes,
1026 states=states, logdata=logdata) 1066 states=states, logdata=logdata,
1067 logdataapis=logdataapis)
1027 return socketproxy(fh, observer) 1068 return socketproxy(fh, observer)
1028 1069
1029 def version(): 1070 def version():
1030 """Return version information if available.""" 1071 """Return version information if available."""
1031 try: 1072 try: